3864 words
19 minutes
HMI DOPSoft Macro Guide
2026-07-04

Complete DOPSoft Macro Guide#

Summary

This note is a complete study guide for DOPSoft Macro. It covers macro types, editor workflow, command categories, programming patterns, safety checks, and many practical macro examples. Images were intentionally removed. Code examples use Expressive Code-style fenced blocks with titles, line markers, line numbers, wrapping, diff blocks, and terminal frames.

What This Chapter Teaches#

This chapter explains how DOPSoft macros work in Delta HMI projects. A macro is a user-written sequence of commands that can run at specific moments: when a button changes state, before or after an object action, when a screen opens or closes, repeatedly while a screen is active, once after HMI startup, or continuously in the background.

The chapter also documents the macro command set:

CategoryMain Purpose
ArithmeticInteger, double word, signed, floating-point, trigonometric, and accumulation operations
Logical OperationBitwise OR, AND, XOR, NOT, left shift, and right shift
Data TransferMove values, copy blocks, fill blocks, handle strings, and store floating-point values
Data ConversionBCD/BIN, Word/DW, byte/word, ASCII/HEX, float/integer, and string formatting
ComparisonIF, ELSEIF, ELSE, ENDIF, GOTO labels, conditional CALL, and floating-point comparison
Flow ControlGOTO, LABEL, CALL, RET, FOR/NEXT loops, and END
Bit SettingSet, clear, toggle, and read bits
CommunicationCOM port initialization, checksum, send/receive characters, station control, and IP changes
DrawingRectangle, line, point, circle, and circle-center calculation
File AccessFileSlot read, write, remove, length, export, and import
OthersTime, errors, delay, system time, history, recipe, alarm, disk format, screen capture, PLC download
Practical takeaway

Use macros for HMI-side helper logic, display calculations, data preparation, formatting, communication helpers, screen behavior, and small automation tasks. Keep time-critical machine control in the PLC whenever possible.

Mental Model of DOPSoft Macros#

A DOPSoft macro is similar to a small script. Each macro contains command lines, and each macro type has a specific trigger. According to the manual, each macro type can contain up to 512 command lines.

A useful mental model is:

Macro execution model
Trigger event happens
DOPSoft selects the macro assigned to that event
The macro reads internal memory, PLC registers, constants, or strings
The macro executes commands line by line
The macro writes results back to internal memory, PLC registers, display objects, files, or communication buffers
The HMI continues with the next object action, screen action, or macro cycle

Address Types#

Address TypeExampleNotes
Internal HMI word$10Stores Word values inside the HMI
Internal HMI bit$10.0Bit 0 of internal word $10
PLC bit/registerM1, device registersDepends on PLC connection and station settings
Constant100, 67.5, "READY"Literal values inside the macro
String memory$100 with character object lengthUsed by character entry/display objects

Word, Double Word, and Signed Values#

Data OptionMeaningPractical Warning
Word16-bit valueOne register/address is used
Double Word / DW32-bit valueTwo consecutive registers/addresses are used
SignedNegative values allowedNeeded for values below zero
UnsignedNo negative valuesGood for counters, raw ADC values, IDs
Floating-pointUsually stored across Double Word spaceDisplay object format must match
Double Word overlap

If $100 is used as a Double Word, $101 is part of that same value. Do not store a separate Word at $101.

Core Syntax Rules#

Assignment#

assignment-basic.mro
$100 = $20
$101 = 67
$102 = $100 + $101

Function Calls#

function-call-pattern.mro
$100 = BCD($20)
$110 = MAX($10, $11)
$120 = FADD(67.5, 34.9) (Signed DW)
$130 = TIMETICK

Command Calls Without Assignment#

command-call-pattern.mro
BITON $10.0
BITOFF $10.1
BMOV($200, $100, 10)
Delay(500)

Comments#

comments.mro
# This comment is not executed
$100 = $20
$101 = $100 * 10
# End of scaling block
recommended-layout.mro
# 1. Read inputs
$100 = $20
$101 = $21
# 2. Normalize or convert values
$110 = TODWORD($100)
$112 = $110 * 10
$114 = $112 / 4095
# 3. Apply decisions
IF $114 > 80
BITON $200.0
ELSE
BITOFF $200.0
ENDIF
# 4. Write display output
$300 = $114

Macro Types and Execution Timing#

Quick Comparison#

Macro TypeTriggerBest UseImportant Limitation
On MacroButton state changes to ONStart command, latch, timestampOnly available for supported button types
Off MacroButton state changes to OFFStop command, reset, timestampRequires state change caused by button action
Before Execute MacroBefore button/input actionValidation and input preparationRuns only when object action is actually executed
After Execute MacroAfter button/input actionPost-processing user inputRuns after the object action
Screen Open MacroOnce when a screen opensScreen initializationOther screen actions wait until it finishes
Screen Close MacroOnce when a screen closesCleanup, save temporary valuesNew screen actions wait until it finishes
Screen Cycle MacroRepeated while screen is openScreen-local monitoringDepends on Macro Cycle Delay
SubmacroCalled by another macroShared reusable logicMust return properly with RET when needed
Initial MacroOnce after HMI startupGlobal initializationRuns only once
Background MacroRepeated during HMI operationContinuous low-load helper tasksExecutes one or several lines per execution cycle
Clock MacroRepeated during HMI operationPeriodic batch logicExecutes all commands in one run

On Macro and Off Macro#

Use On/Off Macros when you need to react to the state transition of supported buttons such as Set ON, Set OFF, Maintained, and Momentary buttons.

button-on-off-macros.mro
# On Macro: button changed to ON
BITON $100.0
$110 = TIMETICK
$111 = 1
# Off Macro: button changed to OFF
BITOFF $100.0
$112 = TIMETICK
$111 = 0

Before Execute Macro#

Use it to validate or prepare data before the object action runs.

before-execute-limit-entry.mro
# Before Execute Macro for a Numeric Entry object
IF $10 < 0
$10 = 0
ELSEIF $10 > 1000
$10 = 1000
ENDIF

After Execute Macro#

Use it to calculate derived values after a button or input element finishes its action.

after-execute-scale-value.mro
# After Execute Macro for a Numeric Entry object
$100 = $10 * 10
$101 = $100 / 100
$102 = TIMETICK

Screen Open Macro#

screen-open-init.mro
# Screen Open Macro
# Reset temporary screen state
FILL($100, 0, 20)
# Load current machine values into display buffer
$200 = $20
$201 = $21
$202 = $22

Screen Close Macro#

screen-close-cleanup.mro
# Screen Close Macro
BITOFF $150.0
BITOFF $150.1
$151 = 0
$152 = TIMETICK

Screen Cycle Macro#

The Screen Cycle Macro repeats while the screen is open. The manual states that the default Macro Cycle Delay is 100 ms.

screen-cycle-monitor.mro
# Screen Cycle Macro
IF $20 > 800
BITON $300.0
$301 = 2
ELSEIF $20 < 100
BITON $300.1
$301 = 1
ELSE
BITOFF $300.0
BITOFF $300.1
$301 = 0
ENDIF

Submacro#

Submacros are similar to subroutines or helper functions.

main-macro-calls-submacro.mro
# Main Macro
CALL 10
$300 = $150
END
# Submacro 10: scale raw value
$150 = $20 * 100
$150 = $150 / 4095
RET

Initial Macro#

initial-macro-startup.mro
# Initial Macro: runs once after HMI startup
FILL($100, 0, 50)
BITOFF $200.0
BITOFF $200.1
$300 = TIMETICK
$301 = 1
$302 = 0

Background Macro#

background-heartbeat.mro
# Background Macro
BITNOT $500.0
$501 = TIMETICK
Delay(1000)
$502 = $502 + 1

Clock Macro#

Clock Macro runs repeatedly and executes its program as a batch. The manual notes a default delay of 100 ms and a maximum delay of 65535 ms.

clock-macro-periodic-check.mro
# Clock Macro
$600 = TIMETICK
IF $600 > 60000
BITON $601.0
ELSE
BITOFF $601.0
ENDIF
$602 = $602 + 1

Macro Editor Workflow#

DOPSoft provides a macro edit window and a toolbar. The important toolbar functions are Open, Save, Update, Cut, Copy, Paste, Syntax Check, Macro Wizard, Insert, Delete, Comment, Command, Variable, and Input Address.

ToolPurposeRecommended Use
OpenImport a macroReuse tested logic from another project
SaveExport a macroKeep backups and reusable snippets
UpdateApply changes and check syntaxUse before closing the editor
Syntax CheckCheck syntax errorsUse often, but do not treat it as full testing
Macro WizardBuild commands through UIGood for avoiding address and parameter errors
Insert/DeleteAdd or remove command linesKeep command order clean
CommentAdd non-executed notesExplain intent, not obvious syntax
Input AddressSelect addresses through UIPrevent typing wrong PLC/HMI addresses
Syntax Check is not enough

Syntax Check verifies command syntax. It does not prove that the logic is correct, that the PLC address is right, or that communication timing is safe.

Suggested Editing Routine#

Terminal-style workflow
1. Write the macro in small sections
2. Add comments for address groups and assumptions
3. Run Syntax Check
4. Update the macro
5. Compile the project
6. Test with safe values
7. Test boundary values
8. Download only after reviewing destructive commands

Command Reference Map#

Full Category Overview#

CategoryCommands
Arithmetic+, -, *, /, %, +-*/, MUL64, ADDSUMW, FADD, FSUB, FMUL, FDIV, FMOD, SIN, COS, TAN, COT, SEC, CSC
Logical Operation`
Data TransferMOV, BMOV, ArrayCopy, FILL, FILLASC, STRCAT, FMOV
Data ConversionBCD, BIN, TODWORD, TOWORD, TOBYTE, SWAP, XCHG, MAX, MIN, TOHEX, TOASC, FCNV, ICNV, SPRINTF
ComparisonIF...THEN GOTO, IF...THEN CALL, IF, ELSEIF, ELSE, ENDIF, FCMP
Flow ControlGOTO LABEL, LABEL, CALL, RET, FOR/NEXT, END
Bit SettingBITON, BITOFF, BITNOT, GETB
CommunicationINITCOM, ADDSUM, XORSUM, PUTCHARS, GETCHARS, SELECTCOM, CLEARCOMBUFFER, CHRCHKSUM, LOCKCOM, UNLOCKCOM, STATIONCHK, STATIONON, STATIONOFF, IPON, IPOFF, IPCHANGE
DrawingRECTANGLE, LINE, POINT, CIRCLE, GetCircleCenter
File AccessFileSlotRead, FileSlotWrite, FileSlotRemove, FileSlotGetLength, FileSlotExport, FileSlotImport
OthersTIMETICK, GETLASTERROR, COMMENT, Delay, GETSYSTEMTIME, SETSYSTEMTIME, GETHISTORY, EXPORT, EXRCP16, IMRCP16, EXRCP32, IMRCP32, EXENRCP, IMENRCP, EXHISTORY, EXALARM, EXALARMGROUP, DISKFORMAT, BMPCAPTURE, PLCDOWNLOAD

Arithmetic Commands#

Arithmetic commands include integer, Double Word, signed, floating-point, trigonometric, and accumulation operations.

Integer Operations#

arithmetic-integer-basic.mro
$1 = 67 + 34
$2 = 67 - 34
$3 = 67 * 34
$4 = 67 / 34
$5 = 67 % 34
arithmetic-engineering-scale.mro
# Convert a raw 0-4095 value into a 0-100 percent value
$100 = $20 * 100
$101 = $100 / 4095
$102 = MAX($101, 0)
$103 = MIN($102, 100)
arithmetic-offset-and-gain.mro
# Engineering value = raw * gain + offset
$100 = $20 * $21
$101 = $100 + $22
$200 = $101

Combined Expression#

arithmetic-combined-expression.mro
# Calculate a compact expression in one line
$1 = $2 + $3 - 57 * $4

64-bit Multiplication#

mul64-example.mro
# Store a large multiplication result
$1 = MUL64(67, 34) (Signed DW)

Accumulation#

addsumw-example.mro
# Example data: $0=1, $2=2, $4=3, $6=4, $8=5 when using DW spacing
$0 = 1
$2 = 2
$4 = 3
$6 = 4
$8 = 5
$50 = ADDSUMW($0, 5)

Floating-point Operations#

floating-point-basic.mro
$100 = FADD(67.5, 34.9) (Signed DW)
$102 = FSUB(67.5, 34.9) (Signed DW)
$104 = FMUL(67.5, 34.9) (Signed DW)
$106 = FDIV(67.5, 34.9) (Signed DW)
floating-point-temperature.mro
# Convert Celsius to Fahrenheit: F = C * 1.8 + 32
$100 = FMUL($20, 1.8) (Signed DW)
$102 = FADD($100, 32.0) (Signed DW)
$200 = $102

Trigonometric Operations#

trigonometric-commands.mro
$10 = SIN(67) (Signed DW)
$12 = COS(67) (Signed DW)
$14 = TAN(67) (Signed DW)
$16 = COT(67) (Signed DW)
$18 = SEC(67) (Signed DW)
$20 = CSC(67) (Signed DW)
Arithmetic rule

Use integer commands for counters, states, raw values, and register manipulation. Use floating-point commands only when the displayed or calculated value truly needs decimals.

Logical Operation Commands#

Logical operations work on binary representation of values.

CommandMeaningExample
``OR
&&AND$10 = $1 && $2
^XOR$10 = $1 ^ $2
NOTBitwise NOT$10 = NOT $1
<<Left shift$10 = $1 << 2
>>Right shift$10 = $1 >> 2
logical-bit-mask.mro
# Use a mask to check whether bit 4 is set
$100 = $20 && 16
IF $100 != 0
BITON $200.0
ELSE
BITOFF $200.0
ENDIF
logical-compose-status-word.mro
# Build a status word from separate bit masks
$100 = 0
IF $10.0 == ON
$100 = $100 | 1
ENDIF
IF $10.1 == ON
$100 = $100 | 2
ENDIF
logical-shift-example.mro
# Move low-level packed data into the expected position
$100 = $20 << 4
$101 = $100 && 255
$102 = $101 >> 2
logical-toggle-with-xor.mro
# Toggle bit mask 1 in a status word
$100 = $100 ^ 1

Data Transfer Commands#

MOV#

mov-simple.mro
$100 = $20
$101 = $21
$102 = $22
$103 = $23

BMOV#

bmov-copy-buffer.mro
# Copy 10 words from $100 to $200
BMOV($200, $100, 10)

ArrayCopy#

arraycopy-concept.mro
# Copy a section of an array-like memory area
# Parameters must be selected according to Macro Wizard and project memory layout
$10 = ArrayCopy($100, 0, $200, 0, 20)

FILL#

fill-clear-block.mro
# Clear 50 words starting at $300
FILL($300, 0, 50)
fill-default-values.mro
# Initialize recipe defaults
FILL($400, 100, 10)
FILL($410, 0, 10)
FILL($420, 1, 5)

FILLASC#

fillasc-status-text.mro
# Store ASCII text for a character display area
FILLASC($500, "READY")
FILLASC($520, "ALARM")
FILLASC($540, "STOP")

STRCAT#

strcat-message.mro
# Join two character areas into one display buffer
$600 = STRCAT($500, $520, 20)

FMOV#

fmov-floating-value.mro
# Store a floating-point constant
$700 = FMOV(67.5) (Signed DW)

Data Conversion Commands#

BCD and BIN#

bcd-bin-conversion.mro
$100 = BCD($20)
$101 = BIN($100)

Word and Double Word Conversion#

word-dword-conversion.mro
# Prepare a Word value for a larger calculation
$100 = TODWORD($20)
$102 = $100 * 1000
$104 = $102 / 4095

Byte and Word Conversion#

byte-word-conversion.mro
# Convert byte-level data into Word-level data
$100 = TOWORD($20, 4)
# Convert Word-level data back into byte-level data
$200 = TOBYTE($100, 4)

SWAP#

swap-byte-order.mro
# Swap high and low bytes for two words
SWAP($10, $20, 2)

XCHG#

xchg-two-blocks.mro
# Exchange two blocks of two words
XCHG($10, $20, 2)

MAX and MIN#

clamp-value.mro
# Clamp $20 into 0..100 range
$100 = MAX($20, 0)
$101 = MIN($100, 100)

ASCII and HEX Conversion#

ascii-hex-conversion.mro
$100 = TOHEX($20)
$110 = TOASC($100)

FCNV and ICNV#

integer-floating-conversion.mro
# Convert integer to floating-point, divide, then convert back if needed
$100 = FCNV($20) (Signed DW)
$102 = FDIV($100, 10.0) (Signed DW)
$104 = ICNV($102)

SPRINTF#

sprintf-status-message.mro
# Format a text message for a character display buffer
$500 = SPRINTF($100, "TEMP=%d", $20)
sprintf-multiple-values.mro
# Conceptual formatted status string
$520 = SPRINTF($200, "P=%d T=%d MODE=%d", $20, $21, $22)
SPRINTF parameter check

Use Macro Wizard to confirm the exact parameter count and destination length in your DOPSoft version. Character display length must be large enough for the final string.

Comparison and Conditional Commands#

Operators#

OperatorMeaning
==Equal
!=Not equal
>Greater than
>=Greater than or equal
<Less than
<=Less than or equal
AND == 0Bitwise AND result equals zero
AND != 0Bitwise AND result is not zero
== ONBit is ON
== OFFBit is OFF

IF / ELSE / ENDIF#

if-else-basic.mro
IF $10 > 800
BITON $200.0
$201 = 2
ELSE
BITOFF $200.0
$201 = 0
ENDIF

ELSEIF Chain#

elseif-state-classification.mro
IF $10 < 100
$300 = 1
ELSEIF $10 < 500
$300 = 2
ELSEIF $10 < 900
$300 = 3
ELSE
$300 = 4
ENDIF

IF THEN GOTO#

if-then-goto-label.mro
IF $1 == $2 THEN GOTO LABEL 1
$100 = $100 + 1
END
LABEL 1
$100 = $200

IF THEN CALL#

if-then-call-submacro.mro
IF $1.0 == ON THEN CALL 5
$300 = $100
END
# Submacro 5
$100 = $20 + $21
RET

AND Condition#

if-and-mask-condition.mro
IF ($10 && 16) != 0
BITON $200.4
ELSE
BITOFF $200.4
ENDIF

Floating-point Comparison#

fcmp-floating-comparison.mro
$50 = FCMP($10, $20) (Signed DW)
IF $50 > 0
$60 = 1
ELSEIF $50 < 0
$60 = -1
ELSE
$60 = 0
ENDIF
Nesting limit

The manual states that nested IF structures support up to 7 layers. For maintainability, prefer Submacros over deeply nested logic.

Flow Control Commands#

GOTO and LABEL#

goto-label-basic.mro
GOTO LABEL 1
$100 = 0
LABEL 1
$100 = $100 + 1

CALL and RET#

call-ret-pattern.mro
CALL 20
$300 = $150
END
# Submacro 20
$150 = $10 + $11
RET

FOR / NEXT#

for-next-counter.mro
FOR 4
$100 = $100 + 1
NEXT
$101 = $100

END#

end-early-exit.mro
IF $1.0 == OFF
END
ENDIF
$100 = $100 + 1

Loop With Data Buffer#

loop-fill-buffer-pattern.mro
# Conceptual loop: repeat a calculation block
$100 = 0
FOR 10
$100 = $100 + 1
$200 = $200 + $100
NEXT
$300 = $200

Bit Setting Commands#

bit-setting-basic.mro
BITON $1.0
BITOFF $1.1
BITNOT $1.2
$10.0 = GETB $2.0
alarm-bit-pattern.mro
# Set alarm bits from thresholds
IF $20 > 900
BITON $100.0
ELSE
BITOFF $100.0
ENDIF
IF $21 == 0
BITON $100.1
ENDIF
mode-toggle-button.mro
# Toggle mode bit and store timestamp
BITNOT $200.0
$201 = TIMETICK
IF $200.0 == ON
$202 = 1
ELSE
$202 = 0
ENDIF

Communication Commands#

Communication macros can initialize COM ports, calculate checksums, send and receive characters, select/clear/lock/unlock COM ports, check stations, and enable/disable/change IP connections.

Communication warning

Do not run heavy communication loops with very short delay. Always use timeout values, clear buffers intentionally, and check return values.

COM Initialization#

initcom-basic.mro
# Parameters must match your port, interface, data bits, parity, stop bits, baud rate, and flow control
$50 = INITCOM(1, 0, 8, 0, 1, 9600, 0)

Send Characters#

putchars-send-frame.mro
# Send 12 bytes from $100 with a timeout
$50 = PUTCHARS($100, 12, 500)
IF $50 == 0
$51 = GETLASTERROR
BITON $300.0
ELSE
BITOFF $300.0
ENDIF

Receive Characters#

getchars-receive-frame.mro
# Clear buffer before receiving a new message
CLEARCOMBUFFER(1, 0)
$60 = GETCHARS($200, 12, 500)
IF $60 == 0
BITON $300.1
ELSE
BITOFF $300.1
ENDIF

Checksums#

checksum-add-xor.mro
$70 = ADDSUM($100, 12)
$71 = XORSUM($100, 12)
string-checksum.mro
# Calculate string length/checksum according to selected parameters
$80 = CHRCHKSUM("READ", 0, 4)

Lock and Unlock COM Port#

lock-unlock-com-transaction.mro
$90 = LOCKCOM(1, 1000)
IF $90 == 1
CLEARCOMBUFFER(1, 0)
$91 = PUTCHARS($100, 8, 500)
$92 = GETCHARS($200, 8, 500)
UNLOCKCOM(1)
ELSE
$93 = GETLASTERROR
BITON $300.2
ENDIF

Station Control#

station-control.mro
$100 = STATIONCHK(1, 1000)
IF $100 == 0
STATIONON(1, 1000)
ELSE
$101 = 1
ENDIF

IP Control#

ip-control.mro
$120 = IPON(192, 168, 0, 1)
IF $120 == 1
$121 = IPCHANGE(192, 168, 0, 20, 502, 1000)
ELSE
$122 = GETLASTERROR
ENDIF

Drawing Commands#

Drawing commands are useful for simple visual marks. For complex interfaces, use DOPSoft objects instead.

drawing-basic.mro
RECTANGLE($50)
LINE($60)
POINT($70)
CIRCLE($80)
drawing-status-overlay.mro
# Conceptual example: draw a marker only during alarm
IF $100.0 == ON
RECTANGLE($50)
LINE($60)
ELSE
$90 = 0
ENDIF
circle-center-concept.mro
# Parameters must be configured using Macro Wizard for the project coordinate system
$100 = GetCircleCenter($20, $21, $22, $23, $24, $25)

File Access Commands#

FileSlot commands work with file storage slots. Use them carefully, especially write/remove/import operations.

Read and Write#

fileslot-read-write.mro
$10 = FileSlotRead(1, $100, 0, 20)
IF $10 == 1
$11 = $100
ENDIF
$12 = FileSlotWrite(1, $200, 0, 20)

Remove and Length#

fileslot-length-remove.mro
$20 = FileSlotGetLength(1, $300)
IF $20 > 0
$21 = FileSlotRemove(1)
ELSE
$21 = 0
ENDIF

Export and Import#

fileslot-export-import.mro
$30 = FileSlotExport(1, $400, 0, 20)
$31 = FileSlotImport(1, $500, 0, 20)
File operation safety

Never call FileSlotWrite, FileSlotRemove, or import commands from a fast repeated macro without a strong condition, confirmation bit, and status feedback.

Other Utility Commands#

Time Tick#

timetick-basic.mro
$100 = TIMETICK
Delay(500)
$101 = TIMETICK

Elapsed Time Measurement#

elapsed-time-measurement.mro
# Start event
$100 = TIMETICK
# Later in the macro or after another event
$101 = TIMETICK
$102 = $101 - $100

GETLASTERROR#

get-last-error-pattern.mro
$10 = FileSlotWrite(1, $100, 0, 20)
IF $10 == 0
$11 = GETLASTERROR
BITON $200.0
ENDIF

Delay#

delay-pattern.mro
BITON $10.0
Delay(500)
BITOFF $10.0
Delay(500)

System Time#

system-time.mro
$100 = GETSYSTEMTIME
SETSYSTEMTIME($200)

Recipe Export and Import#

recipe-import-export.mro
$100 = EXRCP16(1, $200)
$101 = IMRCP16(1, $200)
$102 = EXRCP32(1, $300)
$103 = IMRCP32(1, $300)
$104 = EXENRCP(1, $400)
$105 = IMENRCP(1, $400)

History and Alarm Export#

history-alarm-export.mro
$200 = GETHISTORY(1, $300, $301, $302, $303)
$210 = EXHISTORY(1, $400, 0)
$220 = EXALARM(1, $500)
$221 = EXALARMGROUP(1, $520, 2)

Screen Capture and Destructive Utilities#

destructive-utilities-guarded.mro
# Only run these commands behind an explicit service-mode condition
IF $900.0 == OFF
END
ENDIF
# Examples must be verified through Macro Wizard before real use
$910 = BMPCAPTURE($920)
IF $900.1 == ON
# DISKFORMAT is destructive. Use only with strict confirmation.
DISKFORMAT($930)
ENDIF

Practical Programming Recipes#

Recipe 1: Safe Numeric Entry Clamp#

recipe-safe-entry-clamp.mro
# After Execute Macro for Numeric Entry $10
IF $10 < 0
$10 = 0
ELSEIF $10 > 100
$10 = 100
ENDIF
$100 = $10

Recipe 2: Alarm With Acknowledgement#

recipe-alarm-with-ack.mro
# $20 is process value, $21 is high limit, $30.0 is acknowledge button
IF $20 > $21
BITON $100.0
ELSE
BITOFF $100.0
ENDIF
IF $100.0 == ON
BITON $101.0
ENDIF
IF $30.0 == ON
BITOFF $101.0
ENDIF

Recipe 3: HMI Heartbeat Bit#

recipe-heartbeat.mro
# Background or Clock Macro with a reasonable delay
BITNOT $500.0
$501 = TIMETICK
Delay(1000)

Recipe 4: Display Raw Value as Percent#

recipe-raw-to-percent.mro
# raw input: $20, output percent: $200
$100 = $20 * 100
$101 = $100 / 4095
$102 = MAX($101, 0)
$103 = MIN($102, 100)
$200 = $103

Recipe 5: Mode Selector From Two Buttons#

recipe-mode-selector.mro
# $10.0 = auto button, $10.1 = manual button
IF $10.0 == ON
$100 = 1
BITOFF $10.1
ELSEIF $10.1 == ON
$100 = 2
BITOFF $10.0
ELSE
$100 = 0
ENDIF

Recipe 6: Startup Default Values#

recipe-startup-defaults.mro
# Initial Macro
FILL($100, 0, 50)
$200 = 100
$201 = 500
$202 = 1000
BITOFF $300.0
BITOFF $300.1

Recipe 7: Communication Request With Timeout Status#

recipe-com-request-timeout.mro
$50 = LOCKCOM(1, 1000)
IF $50 == 1
CLEARCOMBUFFER(1, 0)
$51 = PUTCHARS($100, 8, 500)
$52 = GETCHARS($200, 8, 1000)
UNLOCKCOM(1)
IF $52 == 0
$53 = GETLASTERROR
BITON $300.0
ELSE
BITOFF $300.0
ENDIF
ENDIF

Recipe 8: File Write With Confirmation Bit#

recipe-fileslot-write-confirmed.mro
# $10.0 is a one-shot confirmation bit from an HMI button
IF $10.0 == OFF
END
ENDIF
$20 = FileSlotWrite(1, $100, 0, 20)
IF $20 == 0
$21 = GETLASTERROR
BITON $200.0
ENDIF
BITOFF $10.0

Recipe 9: Submacro-Based Scaling Library#

recipe-submacro-scaling-library.mro
# Main macro
CALL 100
CALL 101
END
# Submacro 100: scale pressure raw value
$300 = $20 * 100
$301 = $300 / 4095
$302 = MAX($301, 0)
$303 = MIN($302, 100)
RET
# Submacro 101: scale temperature raw value
$310 = $21 * 200
$311 = $310 / 4095
$312 = $311 - 50
RET
4 collapsed lines
# Reserved for future reusable conversions
# Submacro 102
# Submacro 103
# Submacro 104

Recipe 10: Screen-Local State Machine#

recipe-screen-state-machine.mro
# $100 is state: 0=idle, 1=ready, 2=run, 3=alarm
IF $20.0 == ON
$100 = 3
ELSEIF $10.0 == ON
$100 = 2
ELSEIF $11.0 == ON
$100 = 1
ELSE
$100 = 0
ENDIF
IF $100 == 3
BITON $200.0
ELSE
BITOFF $200.0
ENDIF

Before and After Refactoring Examples#

Replace Repeated Logic With Submacro#

refactor-submacro.diff
$100 = $20 * 100
$101 = $100 / 4095
$200 = $101
$110 = $21 * 100
$111 = $110 / 4095
$201 = $111
CALL 10
CALL 11
$200 = $101
$201 = $111
refactored-submacros.mro
CALL 10
CALL 11
END
# Submacro 10
$100 = $20 * 100
$101 = $100 / 4095
RET
# Submacro 11
$110 = $21 * 100
$111 = $110 / 4095
RET

Guard Destructive Commands#

guard-destructive-command.diff
DISKFORMAT($930)
IF $900.0 == ON
IF $900.1 == ON
DISKFORMAT($930)
ENDIF
ENDIF

Add Error Handling#

add-error-handling.diff
$10 = FileSlotWrite(1, $100, 0, 20)
$10 = FileSlotWrite(1, $100, 0, 20)
IF $10 == 0
$11 = GETLASTERROR
BITON $200.0
ELSE
BITOFF $200.0
ENDIF

Testing and Debugging Workflow#

Test Memory Map#

Memory RangeSuggested Use
$0-$49Temporary values for small macros
$50-$99Communication return values and errors
$100-$199Display buffers and calculated values
$200-$299Status words and alarm bits
$300-$399Recipe and file operation buffers
$400-$499String buffers
$500-$599Background/Clock macro state

Boundary Test Macro#

test-boundary-values.mro
# Test clamp logic with low, normal, and high values
$10 = -1
CALL 30
$11 = 50
CALL 31
$12 = 1001
CALL 32
END

Debug Status Pattern#

debug-status-pattern.mro
$900 = 0
IF $20 > 800
$900 = 1
ENDIF
IF $21 == 0
$901 = GETLASTERROR
ENDIF
$902 = TIMETICK
Safe macro test workflow
Syntax Check
Update macro
Compile project
Run with safe simulated values
Run lower boundary values
Run upper boundary values
Run communication timeout test
Run power-cycle startup test
Review destructive commands before download

Common Mistakes#

MistakeWhy It MattersFix
Using $101 after $100 as DW$101 belongs to $100 DW valueReserve two addresses for DW values
Running communication too fastBuffer and timeout errorsUse delay, locks, and return checks
Missing RET in SubmacroMacro may not return as expectedEnd reusable Submacros with RET
Duplicate labelsGOTO may jump to the wrong placeKeep label numbers unique per macro
Using Screen Cycle for global logicLogic only runs while screen is openUse Clock or Background Macro
No confirmation for file/write/format commandsData loss riskUse service mode and confirmation bits
Too many nested IF blocksHard to maintain and debugSplit into Submacros
No comments for memory rangesFuture edits become dangerousDocument address ownership
Syntax Check onlyLogical errors remain hiddenCompile and test behavior

Final Checklist#

  • Macro type matches the required trigger.
  • The macro does not rely on a screen being open unless that is intended.
  • Word and Double Word addresses do not overlap.
  • Signed, unsigned, floating-point, and display formats are consistent.
  • Submacros end with RET when they must return.
  • Labels are unique within the same macro.
  • Screen Cycle and Clock delays are safe.
  • Communication commands check return values.
  • File, recipe, disk, and PLC download commands are guarded.
  • Syntax Check has passed.
  • Project Compile has passed.
  • Boundary tests were performed.
  • Power-cycle behavior was checked for Initial Macro and persistent states.

FAQ#

Should control logic be written in the HMI macro or PLC?

Safety-critical and time-critical control should remain in the PLC. Use HMI macros for display logic, data preparation, formatting, simple helper tasks, and UI-related automation.

When should I use Screen Cycle Macro instead of Clock Macro?

Use Screen Cycle when the logic only matters while a specific screen is open. Use Clock Macro for repeated global logic that should run regardless of the current screen.

Why does my button macro not execute?

Some button-related macros execute only if the button state changes through the user action. If the state is controlled externally by the PLC or another macro, the trigger may not occur.

Why does my floating-point value display incorrectly?

Check the macro command type, Double Word address allocation, display object Data Type, and display Data Format. Floating values usually require matching DW/floating configuration in the object.

How should I organize many Submacros?

Group them by function. For example, 1-99 for screen helpers, 100-199 for scaling, 200-299 for communication, and 300-399 for file/recipe utilities.

Can I use comments inside macros?

Yes. Use # comments for memory maps, assumptions, and purpose. Avoid commenting obvious single assignments unless the address meaning is important.

References#

  • Delta Electronics, DOPSoft User Manual - Chapter 24: Macro, November 2018.
HMI DOPSoft Macro Guide
https://jamshidzadeh.ir/posts/study/hmi/hmi-dopsoft-macro-guide/
Author
Ali Jamshidzadeh
Published at
2026-07-04