OpenCPLC ⚒️ Forge
Forge is a console app that makes working with OpenCPLC easier.
Its job is to set up your environment so you 👨💻developer can focus on building apps instead of fighting with configs and compilation.
Available as a Python pip package or standalone opencplc.exe from 🚀Releases (in that case add its location to system PATH manually)
pip install opencplc
Just pick a folder (your workspace), open CMD and type:
opencplc -n <project_name> -b <board>
opencplc -n myapp -b uno
This creates a directory (or directory tree) projects/<project_name>.
Two files are created inside: main.c and main.h, the minimal project setup.
Don't delete them or move to subfolders.
When you have more projects, you can switch between them freely:
opencplc <project_name>
opencplc myapp
You can also pick projects by number from list:
opencplc -l # show project list
opencplc 3 # load project #3 from list
Every project owns its makefile and flash.ld, and the makefile in the workspace root points at the active one.
Loading a project regenerates these files: they transform everything (project and framework files: .c, .h, .s) into binary files .bin/.hex that can be flashed to the PLC.
Changing PRO_x values in main.h or the project structure (adding, moving, deleting or renaming files) needs a reload.
make does it by itself: when main.h or the source tree is newer than the makefile, it runs Forge first and then builds.
Editing the body of a function needs no reload, make handles that on its own.
You can also reload by hand, without the project name when it's active or when you're inside its directory:
opencplc <project_name>
opencplc -r
Flags such as -b, -c, -m and -o configure a project only when it's created.
Later the configuration lives in main.h: edit it and reload.
📄 Your files
main.c is yours and Forge never overwrites it.
The skeleton of a PLC board project looks like this:
#include "opencplc.h"
void loop(void)
{
while(1) {
LED_Set(RGB_Green);
delay(1000);
LED_Rst();
delay(1000);
}
}
stack(stack_plc, 256);
stack(stack_dbg, 256);
stack(stack_loop, 1024);
int main(void)
{
thread(PLC_Main, stack_plc); // PLC thread
thread(DBG_Loop, stack_dbg); // logs and console
thread(loop, stack_loop); // your application
vrts_init(); // start thread switching
while(1);
}
Your application runs as a VRTS thread next to the PLC thread and the debugger thread (logs and console). Add your own modules as more files in the project directory and its subfolders.
main.h holds the configuration Forge reads on every load.
The PRO_* definitions describe the board, chip, framework version and memory sizes, while LOG_LEVEL and SYS_CLOCK_FREQ are yours to change.
Extra framework drivers go there too: #define PRO_DRIVERS "shtc3, hd44780".
Here (roughly) ends Forge job, and further work goes like typical embedded systems project using ✨Make.
✨ Make
If you have proper project config and makefile generated by ⚒️Forge, to build and flash program to PLC just open console in workspace and type:
make build # build C project to binary
make flash # upload binary to PLC memory
# or
make run # run = build + flash
make in the workspace root works on the active project.
Any project can be built directly, independently of the active one: make -C projects/myapp.
Full list of targets:
make buildor justmake: Builds C project to.bin,.hex,.elffilesmake flash: Uploads program to PLC (microcontroller) memorymake run: Doesmake build, thenmake flashmake cleanormake clr: Removes built files for the projectmake clean_allormake clr_all: Removes built files for all projectsmake dist: Copies the.hexto the project folder;make dist TAG=1.2.0names it<name>-1.2.0.hexmake erase: Completely wipes microcontroller memory (erase full chip)
Built files land in build/projects/<project_name>/: the .elf, .hex, .bin and .map next to opencplc/ with framework objects and project/ with yours.
Every project compiles the framework on its own, so switching projects never links objects built with another configuration.
After linking Forge reports memory usage:
FLASH 70.7kB / 72kB (98%)
RAM 34.3kB / 36kB (95%)
⚙️ Config
On first project ⚒️Forge creates config file opencplc.json.
It contains:
version: Default OpenCPLC framework version for new projects. Valuelatestmeans newest stable version.stlink: Programmer bound to a project, somake flashhits the right board when several ST-Links are connected. Set withopencplc myapp -s <serial>, clear withopencplc myapp -s. Read the serial from the OpenOCD log ofmake flashwith one programmer connected.available-versions: List of all available framework versions. Set automatically, used offline.
The workspace layout is fixed: projects/ with your projects, opencplc/ with framework versions and build/ with built files.
Copy a project folder manually and it's detected on next run.
🤔 How works?
Who does what: Forge prepares the environment, Make builds and flashes, you write the code.
flowchart LR
CORE[repo Core] -->|clone| FW["opencplc/0.4.3"]
FORGE[Forge] --> GEN["projects/myapp/makefile, flash.ld"]
FW --> GEN
YOU[your code] --> PRO["projects/myapp/main.c"]
GEN --> MAKE[make]
PRO --> MAKE
MAKE --> BIN["build/projects/myapp/myapp.hex"]
First Forge installs the Git client, then (once it knows the project platform) Make, GNU Arm Embedded Toolchain and OpenOCD, and sets system variables if these apps aren't visible from console. For HOST platform, MinGW (GCC for Windows) is installed instead of ARM toolchain. If you don't want anyone messing with your system, install these tools yourself and put them on PATH. When ⚒️Forge installs missing apps, it adds them to system PATH and continues. Restart your console afterward to use them directly.
Then if needed, it clones OpenCPLC framework from repository to opencplc/<version>.
A new project takes the version from opencplc.json or the one given with -f --framework:
opencplc <project_name> --new -f 0.4.3
opencplc <project_name> --new -f develop
📌 Project versioning
Each project stores in main.h the framework version it was created with (definition PRO_VERSION).
That version is used to build it and gets cloned when missing, so old projects compile even after framework update to newer version.
If the clone fails, Forge warns and builds with the workspace default.
To try another version without touching main.h, pass -f when loading the project: it builds with that version once and says so.
🧩 Boards
Ready boards come from the framework: every directory plc/brd/<board>/ with an .ini manifest is a board.
The manifest gives the chip, initial memory and clock of a new project, and drivers the board needs:
chip = STM32G0C1
flash_kB = 492
ram_kB = 144
clock_Hz = 59904000
drivers = max31865
Adding a board means adding a directory to the framework, nothing changes in Forge.
-b custom -c <chip> gives the PLC layer without a board: peripheral mapping and PLC_Main are yours to write.
-c <chip> alone is bare metal: HAL and libraries only.
Extra framework drivers for a project go to main.h: #define PRO_DRIVERS "shtc3, hd44780".
Main Forge function is preparing files needed for project:
projects/<name>/flash.ld: defines RAM and FLASH memory layout (overwrites, STM32 only)projects/<name>/makefile: Contains build, clean and flash rules (overwrites)makefile: points at the active project (overwrites)c_cpp_properties.json: sets header paths and IntelliSense config in VS Code (overwrites)launch.json: configures debugging in VSCode (overwrites)tasks.json: describes tasks like compile or flash (overwrites)settings.json: sets local editor preferences (creates once, not overwritten)extensions.json: suggests useful VSCode extensions (creates once, not overwritten)
There's also bunch of helper functions accessible through smart use of 🚩flags.
🗂️ Workspace structure
workspace/
├─ opencplc.json # workspace config
├─ makefile # active project (generated by Forge)
├─ .vscode/ # VSCode config (generated by Forge)
├─ opencplc/ # framework (downloaded automatically)
│ ├─ 0.4.3/
│ └─ develop/
├─ projects/ # user projects
│ ├─ myapp/
│ │ ├─ main.c
│ │ ├─ main.h
│ │ ├─ makefile # generated by Forge
│ │ └─ flash.ld # generated by Forge, STM32 only
│ ├─ firm/app/ # projects can be nested
│ └─ examples/ # demo examples, `opencplc -e`
└─ build/ # compiled binary files
└─ projects/myapp/
If IntelliSense stops working, use F1 → C/C++: Reset IntelliSense Database.
🖥️ Host
Forge supports Host platform for developing and testing code on PC (Windows/Linux) without embedded hardware:
opencplc -n myapp -c host # desktop project
This creates project that compiles with native GCC (MinGW on Windows) instead of ARM toolchain, and make run starts the program.
Useful for:
- Testing algorithms and logic without hardware
- Developing protocol parsers and data processing
- Unit testing framework components
- Quick prototyping before deploying to PLC
Host platform provides stub implementations for hardware-dependent modules (GPIO, timers, etc.) so code structure remains compatible with STM32 targets.
🚩 Flags
Beyond the basic flags described above, there are a few more worth knowing. Full list:
Basic
name: Project name. Default first argument. Also defines the project path:projects/name, and output files (.bin,.hex,.elf) are tied to it. Can also be a project number from the-llist.-n --new: Creates a new project with the given name.-e --example: Downloads demo examples from the Demo repository intoprojects/examples. Load one like any project:opencplc examples/blinky.-r --reload: Regenerates project files. Withoutnameit takes the active project, or the one whose directory you're in.-d --delete: Deletes the project with the givenname.-g --get: Downloads a project from Git (GitHub, GitLab, ...) or a remote ZIP and adds it as a new project. The second argument (first is the link) can be a reference (branch,tag). Ifnameis not specified, it tries to read it from the@namefield inmain.h.
Hardware config
-b --board: Board from the framework (uno),customfor your own hardware with the PLC layer, ornonefor a bare microcontroller.-c --chip: Microcontroller or platform:STM32G081,STM32G0C1,STM32WB55,HOST(compile for PC). Without-b --board, the project runs without the PLC layer, only HAL and standard framework libraries. Useful for Nucleo boards or custom hardware.-m --memory: Memory in kB:FLASH RAM [RESERVED].RESERVEDis the memory allocated for config and EEPROM, subtracted from FLASH in the linker fileflash.ld. (STM32 only)
Build config
-f --framework: Framework version:latest,develop,0.4.3. For a new project it becomesPRO_VERSION; for an existing one it builds with that version once.-o --opt-level: Compiler optimization level:O0,Og(default),O1,O2,O3. LevelsO2andO3show a warning for STM32 (timing, debugging).-s --stlink: Binds an ST-Link serial to the project;-salone clears the binding.
Info
-l --list: Lists existing projects.-i --info: Returns basic info about the specified or active project, including project and framework versions.-F --framework-versions: Lists all available OpenCPLC framework versions.-v --version: Shows the ⚒️Forge version and repository link.
Tools
-a --assets: Downloads helper materials for design (docs, diagrams). Optionally accepts a folder name as destination.-u --update: Checks for and installs ⚒️Forge updates. Accepts a specific version orlatest.-z --size: Reports FLASH and RAM usage of an.elf;makeuses it after linking.-y --yes: Auto-confirms all prompts (non-interactive mode).
Hash utilities
-hl --hash-list: Generates an enum with DJB2 hashes from a tag list.-ht --hash-title: Enum type name for the hash generator.-hd --hash-define: Uses#defineinstead ofenumfor hash output.
🗑️ Deleting and 💾 copying projects can be done directly from the OS.
Each project stores all the information it needs in main.h, and its presence is auto-detected on startup.
📟 Console
⚒️Forge and ✨Make are console programs. Essential for working with OpenCPLC.
System console is available in many apps like Command Prompt, PowerShell, GIT Bash, even terminal in VSCode. Forge finds the workspace from any directory inside it, so a project directory is a fine place to open the console too.
When something goes wrong:
- Forge lands in the wrong workspace: a stray
opencplc.jsonsits somewhere between the project and the real root, remove it. - Compiler not found right after the tools were installed: the console still has the old
PATH, close it and open a new one. makestops atopencplc -rwith an error about the version or the board:main.hpoints at something this framework doesn't have, fix the entry and runmakeagain.- A project is missing from
-l: its directory has nomain.h.
📋 Usage examples
# Creating new project
opencplc -n myapp -b uno # project for OpenCPLC Uno board
opencplc -n myapp -b uno -m 128 36 # project for Uno with 128kB/36kB memory
opencplc -n myapp -b custom -c STM32G081 # custom hardware with PLC layer (no peripheral mapping)
opencplc -n myapp -c STM32G081 # bare-metal project for STM32G081 (e.g. Nucleo)
opencplc -n myapp -c host # desktop project (Windows/Linux)
# Managing projects
opencplc myapp # load project 'myapp'
opencplc 3 # load project #3 from list
opencplc -r # reload active project
opencplc -l # list all projects
opencplc -i # info about active project
opencplc myapp -s 066AFF49 # bind ST-Link to 'myapp'
# Demo examples
opencplc -e # download examples to projects/examples
opencplc examples/blinky # load example 'blinky'
# Downloading projects
opencplc -g https://github.com/user/repo
opencplc -g https://github.com/user/repo v1.0.0
# Updates
opencplc -u # update Forge to latest version
opencplc -F # show available Core versions
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file opencplc-0.3.1.tar.gz.
File metadata
- Download URL: opencplc-0.3.1.tar.gz
- Upload date:
- Size: 60.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6919de9a776c8ee1184409104682f86ab013c30f946f410bf9921dda45bded07
|
|
| MD5 |
28459d3f4d90fb6451b8ca000b559c02
|
|
| BLAKE2b-256 |
18c93581ca999613b53e8fdb4ebc8116760f471e973865a5b2808705142c848e
|
Provenance
The following attestation bundles were made for opencplc-0.3.1.tar.gz:
Publisher:
publish.yml on OpenCPLC/Forge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
opencplc-0.3.1.tar.gz -
Subject digest:
6919de9a776c8ee1184409104682f86ab013c30f946f410bf9921dda45bded07 - Sigstore transparency entry: 2679594619
- Sigstore integration time:
-
Permalink:
OpenCPLC/Forge@462ee180228fb3cb1511f05d2c0743448e8b8bba -
Branch / Tag:
refs/tags/0.3.1 - Owner: https://github.com/OpenCPLC
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@462ee180228fb3cb1511f05d2c0743448e8b8bba -
Trigger Event:
release
-
Statement type:
File details
Details for the file opencplc-0.3.1-py3-none-any.whl.
File metadata
- Download URL: opencplc-0.3.1-py3-none-any.whl
- Upload date:
- Size: 56.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c9adfc917dc4b4c4992677d03894c4f84a51329fcbd017c553dcac3a660de69
|
|
| MD5 |
8e5867db393e58584ce6ae0dd260721e
|
|
| BLAKE2b-256 |
f0b25fa162377accb7cd775046edd180b88982baa932e75fd43f0ae01c21c3bd
|
Provenance
The following attestation bundles were made for opencplc-0.3.1-py3-none-any.whl:
Publisher:
publish.yml on OpenCPLC/Forge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
opencplc-0.3.1-py3-none-any.whl -
Subject digest:
5c9adfc917dc4b4c4992677d03894c4f84a51329fcbd017c553dcac3a660de69 - Sigstore transparency entry: 2679594883
- Sigstore integration time:
-
Permalink:
OpenCPLC/Forge@462ee180228fb3cb1511f05d2c0743448e8b8bba -
Branch / Tag:
refs/tags/0.3.1 - Owner: https://github.com/OpenCPLC
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@462ee180228fb3cb1511f05d2c0743448e8b8bba -
Trigger Event:
release
-
Statement type: