From a161965f9e7cee8ff74825896a5940be37b16240 Mon Sep 17 00:00:00 2001 From: MerzSebastian Date: Thu, 11 Sep 2025 00:11:26 +0200 Subject: [PATCH 1/8] test --- arduino/arduino.ino | 95 +++++++++++- logic-editor/src/App.tsx | 246 ++++++++++++++++++++++++++++++- logic-editor/src/bytecode-gen.ts | 147 ++++++++++++++++++ 3 files changed, 485 insertions(+), 3 deletions(-) diff --git a/arduino/arduino.ino b/arduino/arduino.ino index 8a78cf5..d225df0 100644 --- a/arduino/arduino.ino +++ b/arduino/arduino.ino @@ -17,6 +17,13 @@ #define OP_PULSE 17 #define OP_TOGGLE 18 #define OP_ANALOG_RANGE 19 +#define OP_ANALOG_COMPARE_GT 20 +#define OP_ANALOG_COMPARE_GE 21 +#define OP_ANALOG_COMPARE_LT 22 +#define OP_ANALOG_COMPARE_LE 23 +#define OP_ANALOG_COMPARE_EQ 24 +#define OP_ANALOG_COMPARE_NE 25 +#define OP_SHIFT_REGISTER 26 const int MAX_INSTRUCTIONS = 300; const int MAX_VARIABLES = 60; @@ -399,7 +406,93 @@ void executeInstructions() { variables[outputVar] = (value >= min && value <= max) ? 1 : 0; break; } - + case OP_ANALOG_COMPARE_GT: { + byte aVar = instructions[pc++]; + byte bVar = instructions[pc++]; + byte outputVar = instructions[pc++]; + variables[outputVar] = variables[aVar] > variables[bVar]; + break; + } + case OP_ANALOG_COMPARE_GE: { + byte aVar = instructions[pc++]; + byte bVar = instructions[pc++]; + byte outputVar = instructions[pc++]; + variables[outputVar] = variables[aVar] >= variables[bVar]; + break; + } + case OP_ANALOG_COMPARE_LT: { + byte aVar = instructions[pc++]; + byte bVar = instructions[pc++]; + byte outputVar = instructions[pc++]; + variables[outputVar] = variables[aVar] < variables[bVar]; + break; + } + case OP_ANALOG_COMPARE_LE: { + byte aVar = instructions[pc++]; + byte bVar = instructions[pc++]; + byte outputVar = instructions[pc++]; + variables[outputVar] = variables[aVar] <= variables[bVar]; + break; + } + case OP_ANALOG_COMPARE_EQ: { + byte aVar = instructions[pc++]; + byte bVar = instructions[pc++]; + byte outputVar = instructions[pc++]; + variables[outputVar] = variables[aVar] == variables[bVar]; + break; + } + case OP_ANALOG_COMPARE_NE: { + byte aVar = instructions[pc++]; + byte bVar = instructions[pc++]; + byte outputVar = instructions[pc++]; + variables[outputVar] = variables[aVar] != variables[bVar]; + break; + } + case OP_SHIFT_REGISTER: { + byte dataVar = instructions[pc++]; + byte clockVar = instructions[pc++]; + byte resetVar = instructions[pc++]; + byte numOutputs = instructions[pc++]; + byte initialOutput = instructions[pc++]; + byte baseOutputVar = instructions[pc++]; + + static bool shiftRegisterInitialized[MAX_VARIABLES] = {false}; + static byte shiftRegisterState[MAX_VARIABLES] = {0}; + + // Initialize on first run + if (!shiftRegisterInitialized[baseOutputVar]) { + shiftRegisterState[baseOutputVar] = 1 << initialOutput; + shiftRegisterInitialized[baseOutputVar] = true; + } + + // Reset logic + if (variables[resetVar]) { + shiftRegisterState[baseOutputVar] = 1 << initialOutput; + } + + // Clock rising edge detection + static bool prevClockState[MAX_VARIABLES] = {false}; + if (variables[clockVar] && !prevClockState[baseOutputVar]) { + // Shift the register + if (variables[dataVar]) { + shiftRegisterState[baseOutputVar] = (shiftRegisterState[baseOutputVar] << 1) | 0x01; + } else { + shiftRegisterState[baseOutputVar] = shiftRegisterState[baseOutputVar] << 1; + } + + // Handle wrap-around + if (shiftRegisterState[baseOutputVar] >= (1 << numOutputs)) { + shiftRegisterState[baseOutputVar] = 1; + } + } + prevClockState[baseOutputVar] = variables[clockVar]; + + // Set output variables + for (byte i = 0; i < numOutputs; i++) { + variables[baseOutputVar + i] = (shiftRegisterState[baseOutputVar] >> i) & 0x01; + } + break; + } } } } \ No newline at end of file diff --git a/logic-editor/src/App.tsx b/logic-editor/src/App.tsx index c85d606..d1f5d9c 100644 --- a/logic-editor/src/App.tsx +++ b/logic-editor/src/App.tsx @@ -47,10 +47,15 @@ const blockTypes = [ { type: 'andNode', label: 'AND', color: 'bg-blue-500' }, // { type: 'orNode', label: 'OR', color: 'bg-purple-500' }, { type: 'notNode', label: 'NOT', color: 'bg-yellow-500' }, + { type: 'nandNode', label: 'NAND', color: 'bg-blue-400' }, + { type: 'norNode', label: 'NOR', color: 'bg-purple-400' }, + { type: 'xorNode', label: 'XOR', color: 'bg-yellow-400' }, { type: 'latchNode', label: 'LATCH', color: 'bg-orange-500' }, { type: 'pulseNode', label: 'PULSE (beta)', color: 'bg-cyan-500' }, { type: 'toggleNode', label: 'TOGGLE', color: 'bg-pink-500' }, { type: 'analogRangeNode', label: 'ANALOG RANGE', color: 'bg-teal-500' }, + { type: 'analogComparerNode', label: 'ANALOG COMPARER', color: 'bg-indigo-500' }, + { type: 'shiftRegisterNode', label: 'SHIFT REGISTER', color: 'bg-purple-600' }, ]; // === Node Definitions === @@ -162,6 +167,211 @@ function PulseNode({ data, id }: any) { ); } +function NandNode({ data, id }: any) { + const { inputs = 2 } = data; + const handleSpacing = 15; + const baseHeight = 50; + const dynamicHeight = baseHeight + (inputs - 3) * handleSpacing; + + return ( +
+
NAND
+ {Array.from({ length: inputs }).map((_, idx) => ( + + ))} + +
+ Inputs: + data.onChangeInputs(id, parseInt(e.target.value))} + className={`${inputClasses} w-8 ml-2`} + /> +
+
+ ); +} + +function NorNode({ data, id }: any) { + const { inputs = 2 } = data; + const handleSpacing = 15; + const baseHeight = 50; + const dynamicHeight = baseHeight + (inputs - 3) * handleSpacing; + + return ( +
+
NOR
+ {Array.from({ length: inputs }).map((_, idx) => ( + + ))} + +
+ Inputs: + data.onChangeInputs(id, parseInt(e.target.value))} + className={`${inputClasses} w-8 ml-2`} + /> +
+
+ ); +} + +function XorNode({ data, id }: any) { + const { inputs = 2 } = data; + const handleSpacing = 15; + const baseHeight = 50; + const dynamicHeight = baseHeight + (inputs - 3) * handleSpacing; + + return ( +
+
XOR
+ {Array.from({ length: inputs }).map((_, idx) => ( + + ))} + +
+ Inputs: + data.onChangeInputs(id, parseInt(e.target.value))} + className={`${inputClasses} w-8 ml-2`} + /> +
+
+ ); +} + +function AnalogComparerNode({ data, id }: any) { + return ( +
+
ANALOG COMPARER
+ + +
A
+
B
+ + + + +
+ ); +} + +function ShiftRegisterNode({ data, id }: any) { + const outputs = data.outputs || 4; + const handleSpacing = 15; + const baseHeight = 80; + const dynamicHeight = baseHeight + (outputs - 4) * handleSpacing; + + return ( +
+
SHIFT REGISTER
+ + {/* Input handles */} + + + + +
data
+
clock
+
reset
+ + {/* Configuration */} +
+
+ Outputs: + data.onChangeOutputs(id, parseInt(e.target.value))} + className={`${inputClasses} ml-1 w-10`} + /> +
+
+ Initial: + +
+
+ + {/* Output handles */} + {Array.from({ length: outputs }).map((_, idx) => ( + + ))} +
+ ); +} + function InputNode({ data, id }: any) { const pins = [ ...((boards as any)[data.selectedBoard]?.digital || []), @@ -343,12 +553,41 @@ export default function App() { andNode: AndNode, orNode: OrNode, notNode: NotNode, + nandNode: NandNode, + norNode: NorNode, + xorNode: XorNode, latchNode: LatchNode, pulseNode: PulseNode, toggleNode: ToggleNode, analogRangeNode: AnalogRangeNode, + analogComparerNode: AnalogComparerNode, + shiftRegisterNode: ShiftRegisterNode, }), []); + const handleComparisonTypeChange = useCallback((nodeId: string, comparisonType: string) => { + setNodes((nds) => + nds.map((n) => + n.id === nodeId ? { ...n, data: { ...n.data, comparisonType } } : n + ) + ); + }, [setNodes]); + + const handleOutputsChange = useCallback((nodeId: string, outputs: number) => { + setNodes((nds) => + nds.map((n) => + n.id === nodeId ? { ...n, data: { ...n.data, outputs } } : n + ) + ); + }, [setNodes]); + + const handleInitialOutputChange = useCallback((nodeId: string, initialOutput: number) => { + setNodes((nds) => + nds.map((n) => + n.id === nodeId ? { ...n, data: { ...n.data, initialOutput } } : n + ) + ); + }, [setNodes]); + const handleMinChange = useCallback((nodeId: string, min: number) => { setNodes((nds) => nds.map((n) => @@ -525,7 +764,7 @@ export default function App() { }; const getArduinoInoFile = async () => { - const githubUrl = 'https://raw.githubusercontent.com/MerzSebastian/OpenPLC/refs/heads/main/arduino/arduino.ino'; + const githubUrl = 'https://raw.githubusercontent.com/MerzSebastian/OpenPLC/refs/heads/feature/add-nodes/arduino/arduino.ino'; const response = await fetch(githubUrl); return await response.text(); } @@ -731,12 +970,15 @@ export default function App() { ...n.data, selectedBoard, onChangePin: handlePinChange, - onChangeInputs: n.type === 'andNode' || n.type === 'orNode' ? handleInputsChange : undefined, + onChangeInputs: n.type === 'andNode' || n.type === 'orNode' || n.type === 'nandNode' || n.type === 'norNode' || n.type === 'xorNode' ? handleInputsChange : undefined, onChangeInitialState: n.type === 'latchNode' || n.type === 'toggleNode' ? handleInitialStateChange : undefined, onChangePulseLength: n.type === 'pulseNode' ? handlePulseLengthChange : undefined, onChangeInterval: n.type === 'pulseNode' ? handleIntervalChange : undefined, onChangeMin: n.type === 'analogRangeNode' ? handleMinChange : undefined, onChangeMax: n.type === 'analogRangeNode' ? handleMaxChange : undefined, + onChangeComparisonType: n.type === 'analogComparerNode' ? handleComparisonTypeChange : undefined, + onChangeOutputs: n.type === 'shiftRegisterNode' ? handleOutputsChange : undefined, + onChangeInitialOutput: n.type === 'shiftRegisterNode' ? handleInitialOutputChange : undefined, } }))} edges={edges} diff --git a/logic-editor/src/bytecode-gen.ts b/logic-editor/src/bytecode-gen.ts index 045319e..1cf1358 100644 --- a/logic-editor/src/bytecode-gen.ts +++ b/logic-editor/src/bytecode-gen.ts @@ -15,6 +15,13 @@ export const OP_LATCH = 16; export const OP_PULSE = 17; export const OP_TOGGLE = 18; export const OP_ANALOG_RANGE = 19; +export const OP_ANALOG_COMPARE_GT = 20; +export const OP_ANALOG_COMPARE_GE = 21; +export const OP_ANALOG_COMPARE_LT = 22; +export const OP_ANALOG_COMPARE_LE = 23; +export const OP_ANALOG_COMPARE_EQ = 24; +export const OP_ANALOG_COMPARE_NE = 25; +export const OP_SHIFT_REGISTER = 26; export const OP_DELAY = 30; // Types for the logic configuration @@ -32,6 +39,9 @@ interface NodeData { initialState?: number; pulseLength?: number; interval?: number; + comparisonType?: string; + outputs?: number; + initialOutput?: number; } interface Node { @@ -349,6 +359,143 @@ export function generateBytecode(config: LogicConfig): number[] { } break; } + case 'nandNode': { + const inputVars: number[] = []; + for (const edge of edges) { + if (edge.target === nodeId) { + const sourceNodeId = edge.source; + if (varIndexMap[sourceNodeId] !== undefined) { + inputVars.push(varIndexMap[sourceNodeId]); + } + } + } + + if (inputVars.length >= 2 && varIndexMap[nodeId] !== undefined) { + instructions.push(OP_NAND); + instructions.push(inputVars.length); + for (const inputVar of inputVars) { + instructions.push(inputVar); + } + instructions.push(varIndexMap[nodeId]); + } + break; + } + case 'norNode': { + const inputVars: number[] = []; + for (const edge of edges) { + if (edge.target === nodeId) { + const sourceNodeId = edge.source; + if (varIndexMap[sourceNodeId] !== undefined) { + inputVars.push(varIndexMap[sourceNodeId]); + } + } + } + + if (inputVars.length >= 2 && varIndexMap[nodeId] !== undefined) { + instructions.push(OP_NOR); + instructions.push(inputVars.length); + for (const inputVar of inputVars) { + instructions.push(inputVar); + } + instructions.push(varIndexMap[nodeId]); + } + break; + } + case 'xorNode': { + const inputVars: number[] = []; + for (const edge of edges) { + if (edge.target === nodeId) { + const sourceNodeId = edge.source; + if (varIndexMap[sourceNodeId] !== undefined) { + inputVars.push(varIndexMap[sourceNodeId]); + } + } + } + + if (inputVars.length >= 2 && varIndexMap[nodeId] !== undefined) { + instructions.push(OP_XOR); + instructions.push(inputVars.length); + for (const inputVar of inputVars) { + instructions.push(inputVar); + } + instructions.push(varIndexMap[nodeId]); + } + break; + } + case 'analogComparerNode': { + // Find inputs A and B + let aVar = -1; + let bVar = -1; + + for (const edge of edges) { + if (edge.target === nodeId) { + const sourceNodeId = edge.source; + if (varIndexMap[sourceNodeId] !== undefined) { + if (edge.targetHandle === 'a') { + aVar = varIndexMap[sourceNodeId]; + } else if (edge.targetHandle === 'b') { + bVar = varIndexMap[sourceNodeId]; + } + } + } + } + + if (aVar >= 0 && bVar >= 0 && varIndexMap[nodeId] !== undefined) { + const comparisonType = node.data.comparisonType || '>'; + let opcode; + + switch (comparisonType) { + case '>': opcode = OP_ANALOG_COMPARE_GT; break; + case '>=': opcode = OP_ANALOG_COMPARE_GE; break; + case '<': opcode = OP_ANALOG_COMPARE_LT; break; + case '<=': opcode = OP_ANALOG_COMPARE_LE; break; + case '==': opcode = OP_ANALOG_COMPARE_EQ; break; + case '!=': opcode = OP_ANALOG_COMPARE_NE; break; + default: opcode = OP_ANALOG_COMPARE_GT; + } + + instructions.push(opcode); + instructions.push(aVar); + instructions.push(bVar); + instructions.push(varIndexMap[nodeId]); + } + break; + } + case 'shiftRegisterNode': { + // Find data, clock, and reset inputs + let dataVar = -1; + let clockVar = -1; + let resetVar = -1; + + for (const edge of edges) { + if (edge.target === nodeId) { + const sourceNodeId = edge.source; + if (varIndexMap[sourceNodeId] !== undefined) { + if (edge.targetHandle === 'data') { + dataVar = varIndexMap[sourceNodeId]; + } else if (edge.targetHandle === 'clock') { + clockVar = varIndexMap[sourceNodeId]; + } else if (edge.targetHandle === 'reset') { + resetVar = varIndexMap[sourceNodeId]; + } + } + } + } + + if (dataVar >= 0 && clockVar >= 0 && resetVar >= 0 && varIndexMap[nodeId] !== undefined) { + const outputs = node.data.outputs || 4; + const initialOutput = node.data.initialOutput || 0; + + instructions.push(OP_SHIFT_REGISTER); + instructions.push(dataVar); + instructions.push(clockVar); + instructions.push(resetVar); + instructions.push(outputs); + instructions.push(initialOutput); + instructions.push(varIndexMap[nodeId]); // Base variable index for outputs + } + break; + } case 'orNode': { const inputVars: number[] = []; for (const edge of edges) { From b26317504d77ab547288d3408fbb010166531ef5 Mon Sep 17 00:00:00 2001 From: MerzSebastian Date: Thu, 11 Sep 2025 00:24:31 +0200 Subject: [PATCH 2/8] small fix --- arduino/arduino.ino | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/arduino/arduino.ino b/arduino/arduino.ino index d225df0..dd58fa2 100644 --- a/arduino/arduino.ino +++ b/arduino/arduino.ino @@ -410,42 +410,44 @@ void executeInstructions() { byte aVar = instructions[pc++]; byte bVar = instructions[pc++]; byte outputVar = instructions[pc++]; - variables[outputVar] = variables[aVar] > variables[bVar]; + variables[outputVar] = (variables[aVar] > variables[bVar]) ? 1 : 0; break; } case OP_ANALOG_COMPARE_GE: { byte aVar = instructions[pc++]; byte bVar = instructions[pc++]; byte outputVar = instructions[pc++]; - variables[outputVar] = variables[aVar] >= variables[bVar]; + variables[outputVar] = (variables[aVar] >= variables[bVar]) ? 1 : 0; break; } case OP_ANALOG_COMPARE_LT: { byte aVar = instructions[pc++]; byte bVar = instructions[pc++]; byte outputVar = instructions[pc++]; - variables[outputVar] = variables[aVar] < variables[bVar]; + variables[outputVar] = (variables[aVar] < variables[bVar]) ? 1 : 0; break; } case OP_ANALOG_COMPARE_LE: { byte aVar = instructions[pc++]; byte bVar = instructions[pc++]; byte outputVar = instructions[pc++]; - variables[outputVar] = variables[aVar] <= variables[bVar]; + variables[outputVar] = (variables[aVar] <= variables[bVar]) ? 1 : 0; break; } case OP_ANALOG_COMPARE_EQ: { byte aVar = instructions[pc++]; byte bVar = instructions[pc++]; byte outputVar = instructions[pc++]; - variables[outputVar] = variables[aVar] == variables[bVar]; + // Add a small tolerance for analog value comparisons + variables[outputVar] = (abs(variables[aVar] - variables[bVar]) < 5) ? 1 : 0; break; } case OP_ANALOG_COMPARE_NE: { byte aVar = instructions[pc++]; byte bVar = instructions[pc++]; byte outputVar = instructions[pc++]; - variables[outputVar] = variables[aVar] != variables[bVar]; + // Add a small tolerance for analog value comparisons + variables[outputVar] = (abs(variables[aVar] - variables[bVar]) >= 5) ? 1 : 0; break; } case OP_SHIFT_REGISTER: { @@ -493,6 +495,7 @@ void executeInstructions() { } break; } + } } } \ No newline at end of file From f747e5e8f402763637b5e93be9a8dad5aee871bd Mon Sep 17 00:00:00 2001 From: MerzSebastian Date: Thu, 11 Sep 2025 21:48:21 +0200 Subject: [PATCH 3/8] Update bytecode-gen.ts --- logic-editor/src/bytecode-gen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic-editor/src/bytecode-gen.ts b/logic-editor/src/bytecode-gen.ts index 1cf1358..f8cbc90 100644 --- a/logic-editor/src/bytecode-gen.ts +++ b/logic-editor/src/bytecode-gen.ts @@ -198,7 +198,7 @@ export function generateBytecode(config: LogicConfig): number[] { visited.add(nodeId); const currentNode = nodeDict[nodeId]; - if (currentNode.type === 'analogRangeNode') return true; + if (currentNode.type === 'analogRangeNode' || currentNode.type === 'analogComparerNode') return true; for (const neighbor of graph[nodeId]) { if (checkConnectedToAnalog(neighbor, visited)) return true; From e1e506a64dfeb0a68e7379e8732fba1be83c218b Mon Sep 17 00:00:00 2001 From: MerzSebastian Date: Thu, 11 Sep 2025 22:01:25 +0200 Subject: [PATCH 4/8] Update App.test.tsx --- logic-editor/src/App.test.tsx | 111 ++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) diff --git a/logic-editor/src/App.test.tsx b/logic-editor/src/App.test.tsx index 5dcae2d..7b86263 100644 --- a/logic-editor/src/App.test.tsx +++ b/logic-editor/src/App.test.tsx @@ -727,3 +727,114 @@ test('larger project without OR. connecting two buttons to same input', () => { expect(bytecodeToString(generateBytecode(test as any))).toEqual(expectedBytecode); }); + +test('analogComparerNode test', () => { + const test = { + "nodes": [ + { + "id": "dndnode_0", + "type": "analogComparerNode", + "position": { + "x": 355.08583015996766, + "y": 267.65848888782415 + }, + "data": { + "label": "analogComparerNode", + "inputs": 2, + "selectedBoard": "arduino_nano" + }, + "width": 160, + "height": 103 + }, + { + "id": "dndnode_1", + "type": "inputNode", + "position": { + "x": 177.975220378801, + "y": 253.12988417921287 + }, + "data": { + "label": "inputNode", + "inputs": 2, + "selectedBoard": "arduino_nano", + "pin": "14" + }, + "width": 97, + "height": 59, + "selected": false, + "dragging": false, + "positionAbsolute": { + "x": 177.975220378801, + "y": 253.12988417921287 + } + }, + { + "id": "dndnode_2", + "type": "inputNode", + "position": { + "x": 173.82419046205496, + "y": 319.5463628471503 + }, + "data": { + "label": "inputNode", + "inputs": 2, + "selectedBoard": "arduino_nano", + "pin": "15" + }, + "width": 97, + "height": 59, + "selected": false, + "dragging": false, + "positionAbsolute": { + "x": 173.82419046205496, + "y": 319.5463628471503 + } + }, + { + "id": "dndnode_3", + "type": "outputNode", + "position": { + "x": 569.5557091918491, + "y": 304.3259198190813 + }, + "data": { + "label": "outputNode", + "inputs": 2, + "selectedBoard": "arduino_nano", + "pin": "13" + }, + "width": 97, + "height": 59, + "selected": false, + "dragging": false + } + ], + "edges": [ + { + "source": "dndnode_1", + "sourceHandle": "out", + "target": "dndnode_0", + "targetHandle": "a", + "id": "reactflow__edge-dndnode_1out-dndnode_0a" + }, + { + "source": "dndnode_2", + "sourceHandle": "out", + "target": "dndnode_0", + "targetHandle": "b", + "id": "reactflow__edge-dndnode_2out-dndnode_0b" + }, + { + "source": "dndnode_0", + "sourceHandle": "out", + "target": "dndnode_3", + "targetHandle": "in", + "id": "reactflow__edge-dndnode_0out-dndnode_3in" + } + ], + "board": "arduino_nano" +}; + const expectedBytecode = "1,14,1,15,2,13,5,14,0,5,15,1,20,0,1,2,4,13,2"; + + expect(bytecodeToString(generateBytecode(test as any))).toEqual(expectedBytecode); +}); From 35235bc1036dec024925253cb98ef2e8fcc63496 Mon Sep 17 00:00:00 2001 From: MerzSebastian Date: Thu, 11 Sep 2025 22:15:08 +0200 Subject: [PATCH 5/8] fix shift register block --- arduino/arduino.ino | 24 +++++++++++++----------- logic-editor/src/bytecode-gen.ts | 7 ++++++- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/arduino/arduino.ino b/arduino/arduino.ino index dd58fa2..e53b34d 100644 --- a/arduino/arduino.ino +++ b/arduino/arduino.ino @@ -459,7 +459,8 @@ void executeInstructions() { byte baseOutputVar = instructions[pc++]; static bool shiftRegisterInitialized[MAX_VARIABLES] = {false}; - static byte shiftRegisterState[MAX_VARIABLES] = {0}; + static unsigned long shiftRegisterState[MAX_VARIABLES] = {0}; + static bool prevClockState[MAX_VARIABLES] = {false}; // Initialize on first run if (!shiftRegisterInitialized[baseOutputVar]) { @@ -467,35 +468,36 @@ void executeInstructions() { shiftRegisterInitialized[baseOutputVar] = true; } - // Reset logic + // Reset logic - active high if (variables[resetVar]) { shiftRegisterState[baseOutputVar] = 1 << initialOutput; } // Clock rising edge detection - static bool prevClockState[MAX_VARIABLES] = {false}; - if (variables[clockVar] && !prevClockState[baseOutputVar]) { - // Shift the register + bool currentClockState = variables[clockVar]; + if (currentClockState && !prevClockState[baseOutputVar]) { + // On rising edge of clock, shift the data if (variables[dataVar]) { + // Shift left and set LSB to 1 shiftRegisterState[baseOutputVar] = (shiftRegisterState[baseOutputVar] << 1) | 0x01; } else { - shiftRegisterState[baseOutputVar] = shiftRegisterState[baseOutputVar] << 1; + // Shift left and set LSB to 0 + shiftRegisterState[baseOutputVar] = (shiftRegisterState[baseOutputVar] << 1) & ~0x01; } - // Handle wrap-around + // Handle wrap-around for the number of outputs if (shiftRegisterState[baseOutputVar] >= (1 << numOutputs)) { - shiftRegisterState[baseOutputVar] = 1; + shiftRegisterState[baseOutputVar] = 1; // Reset to first output } } - prevClockState[baseOutputVar] = variables[clockVar]; + prevClockState[baseOutputVar] = currentClockState; - // Set output variables + // Set output variables (one for each output) for (byte i = 0; i < numOutputs; i++) { variables[baseOutputVar + i] = (shiftRegisterState[baseOutputVar] >> i) & 0x01; } break; } - } } } \ No newline at end of file diff --git a/logic-editor/src/bytecode-gen.ts b/logic-editor/src/bytecode-gen.ts index f8cbc90..762e274 100644 --- a/logic-editor/src/bytecode-gen.ts +++ b/logic-editor/src/bytecode-gen.ts @@ -482,7 +482,12 @@ export function generateBytecode(config: LogicConfig): number[] { } } - if (dataVar >= 0 && clockVar >= 0 && resetVar >= 0 && varIndexMap[nodeId] !== undefined) { + // If reset is not connected, use a default variable (0) + if (resetVar === -1) { + resetVar = 0; // Use variable 0 as default (should be 0) + } + + if (dataVar >= 0 && clockVar >= 0 && varIndexMap[nodeId] !== undefined) { const outputs = node.data.outputs || 4; const initialOutput = node.data.initialOutput || 0; From 0e5bb2e847bf7b1cc247238f2de76dd32bf687f0 Mon Sep 17 00:00:00 2001 From: MerzSebastian Date: Thu, 11 Sep 2025 22:22:48 +0200 Subject: [PATCH 6/8] Update arduino.ino --- arduino/arduino.ino | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arduino/arduino.ino b/arduino/arduino.ino index e53b34d..c6d6349 100644 --- a/arduino/arduino.ino +++ b/arduino/arduino.ino @@ -482,11 +482,12 @@ void executeInstructions() { shiftRegisterState[baseOutputVar] = (shiftRegisterState[baseOutputVar] << 1) | 0x01; } else { // Shift left and set LSB to 0 - shiftRegisterState[baseOutputVar] = (shiftRegisterState[baseOutputVar] << 1) & ~0x01; + shiftRegisterState[baseOutputVar] = (shiftRegisterState[baseOutputVar] << 1) & 0xFFFFFFFE; } // Handle wrap-around for the number of outputs - if (shiftRegisterState[baseOutputVar] >= (1 << numOutputs)) { + unsigned long maxState = 1 << numOutputs; + if (shiftRegisterState[baseOutputVar] >= maxState) { shiftRegisterState[baseOutputVar] = 1; // Reset to first output } } From 95e11a55da56f9efc5ec23d75bb45c830019dec6 Mon Sep 17 00:00:00 2001 From: MerzSebastian Date: Thu, 11 Sep 2025 22:30:43 +0200 Subject: [PATCH 7/8] fix --- arduino/arduino.ino | 8 +++++--- logic-editor/src/bytecode-gen.ts | 8 ++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/arduino/arduino.ino b/arduino/arduino.ino index c6d6349..e26bd8f 100644 --- a/arduino/arduino.ino +++ b/arduino/arduino.ino @@ -453,7 +453,7 @@ void executeInstructions() { case OP_SHIFT_REGISTER: { byte dataVar = instructions[pc++]; byte clockVar = instructions[pc++]; - byte resetVar = instructions[pc++]; + byte resetVar = instructions[pc++]; // This can be 255 now byte numOutputs = instructions[pc++]; byte initialOutput = instructions[pc++]; byte baseOutputVar = instructions[pc++]; @@ -468,8 +468,8 @@ void executeInstructions() { shiftRegisterInitialized[baseOutputVar] = true; } - // Reset logic - active high - if (variables[resetVar]) { + // Reset logic - only apply if reset is connected (not 255) + if (resetVar != 255 && variables[resetVar]) { shiftRegisterState[baseOutputVar] = 1 << initialOutput; } @@ -497,8 +497,10 @@ void executeInstructions() { for (byte i = 0; i < numOutputs; i++) { variables[baseOutputVar + i] = (shiftRegisterState[baseOutputVar] >> i) & 0x01; } + break; } + } } } \ No newline at end of file diff --git a/logic-editor/src/bytecode-gen.ts b/logic-editor/src/bytecode-gen.ts index 762e274..5f5b115 100644 --- a/logic-editor/src/bytecode-gen.ts +++ b/logic-editor/src/bytecode-gen.ts @@ -482,9 +482,9 @@ export function generateBytecode(config: LogicConfig): number[] { } } - // If reset is not connected, use a default variable (0) + // If reset is not connected, use a special value (255) to indicate no reset if (resetVar === -1) { - resetVar = 0; // Use variable 0 as default (should be 0) + resetVar = 255; // Special value meaning "no reset connected" } if (dataVar >= 0 && clockVar >= 0 && varIndexMap[nodeId] !== undefined) { @@ -494,10 +494,10 @@ export function generateBytecode(config: LogicConfig): number[] { instructions.push(OP_SHIFT_REGISTER); instructions.push(dataVar); instructions.push(clockVar); - instructions.push(resetVar); + instructions.push(resetVar); // This can be 255 now instructions.push(outputs); instructions.push(initialOutput); - instructions.push(varIndexMap[nodeId]); // Base variable index for outputs + instructions.push(varIndexMap[nodeId]); } break; } From a7e6d96e39f15d64407d5f673ac45933c3b6938e Mon Sep 17 00:00:00 2001 From: MerzSebastian Date: Thu, 11 Sep 2025 22:59:30 +0200 Subject: [PATCH 8/8] test --- arduino/arduino.ino | 24 ++++++++++++------------ logic-editor/src/App.tsx | 4 ++-- logic-editor/src/bytecode-gen.ts | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/arduino/arduino.ino b/arduino/arduino.ino index e26bd8f..9ca3ae0 100644 --- a/arduino/arduino.ino +++ b/arduino/arduino.ino @@ -453,41 +453,42 @@ void executeInstructions() { case OP_SHIFT_REGISTER: { byte dataVar = instructions[pc++]; byte clockVar = instructions[pc++]; - byte resetVar = instructions[pc++]; // This can be 255 now + byte resetVar = instructions[pc++]; byte numOutputs = instructions[pc++]; - byte initialOutput = instructions[pc++]; + byte initialState = instructions[pc++]; byte baseOutputVar = instructions[pc++]; static bool shiftRegisterInitialized[MAX_VARIABLES] = {false}; - static unsigned long shiftRegisterState[MAX_VARIABLES] = {0}; + static uint8_t shiftRegisterState[MAX_VARIABLES] = {0}; static bool prevClockState[MAX_VARIABLES] = {false}; // Initialize on first run if (!shiftRegisterInitialized[baseOutputVar]) { - shiftRegisterState[baseOutputVar] = 1 << initialOutput; + // Set only the initial output bit high, others low + shiftRegisterState[baseOutputVar] = (1 << initialState); shiftRegisterInitialized[baseOutputVar] = true; } - // Reset logic - only apply if reset is connected (not 255) - if (resetVar != 255 && variables[resetVar]) { - shiftRegisterState[baseOutputVar] = 1 << initialOutput; + // Reset logic - only if reset is connected (not 255) and reset is HIGH + if (resetVar != 255 && variables[resetVar] == HIGH) { + // Reset to initial state (only the initial output bit high) + shiftRegisterState[baseOutputVar] = (1 << initialState); } // Clock rising edge detection bool currentClockState = variables[clockVar]; if (currentClockState && !prevClockState[baseOutputVar]) { - // On rising edge of clock, shift the data + // On rising edge of clock, shift the register if (variables[dataVar]) { // Shift left and set LSB to 1 shiftRegisterState[baseOutputVar] = (shiftRegisterState[baseOutputVar] << 1) | 0x01; } else { // Shift left and set LSB to 0 - shiftRegisterState[baseOutputVar] = (shiftRegisterState[baseOutputVar] << 1) & 0xFFFFFFFE; + shiftRegisterState[baseOutputVar] = (shiftRegisterState[baseOutputVar] << 1) & 0xFE; } // Handle wrap-around for the number of outputs - unsigned long maxState = 1 << numOutputs; - if (shiftRegisterState[baseOutputVar] >= maxState) { + if (shiftRegisterState[baseOutputVar] >= (1 << numOutputs)) { shiftRegisterState[baseOutputVar] = 1; // Reset to first output } } @@ -497,7 +498,6 @@ void executeInstructions() { for (byte i = 0; i < numOutputs; i++) { variables[baseOutputVar + i] = (shiftRegisterState[baseOutputVar] >> i) & 0x01; } - break; } diff --git a/logic-editor/src/App.tsx b/logic-editor/src/App.tsx index d1f5d9c..0181ff8 100644 --- a/logic-editor/src/App.tsx +++ b/logic-editor/src/App.tsx @@ -346,8 +346,8 @@ function ShiftRegisterNode({ data, id }: any) {
Initial: