PLC+Arduino实战笔记05:金属到位,LED才亮
PLC+Arduino实战笔记05:金属到位,LED才亮
前几篇已经完成了PLC启动、停止、自保持、3秒延时和状态灯显示。这一篇继续往前走一步:加入接近开关,让PLC根据现场检测结果决定是否允许Arduino点亮LED。arduino C和C#的写法在前面部分很相似,实际是会了C,学工业C#会容易的多。本系列将通过52节课直达工业机器视觉前置课程。
本次实验的核心是:准备完成,不代表立即执行;工件到位后,才允许输出。

一、本次要实现的效果
保留前面的启停与延时逻辑,只增加一个“金属工件到位”的条件。
按下启动按钮
↓
PLC保持启动状态
↓
等待3秒,准备完成
↓
检测金属是否到位
├─ 没有金属 → Arduino LED不亮
└─ 金属到位 → Arduino LED亮
准备完成以后,移走金属,Arduino LED熄灭;再次靠近,LED再次点亮。按停止按钮,则撤销启动状态和输出。
这一篇不加电机、计数和超时报警,先把“检测条件控制输出”这一条链路跑通。
二、实验硬件与分工
本次使用的硬件:
- 西门子S7-200 SMART CR40 PLC
- 金属检测用接近开关
- NPN转PNP信号转换器
- 24V输入光耦隔离模块
- Arduino
- LED及其配套限流电路
- 原有启动、停止按钮和状态指示灯
各部分分工如下:
| 部件 | 本次作用 |
|---|---|
| 接近开关 | 检测金属工件是否到位 |
| NPN转PNP转换器 | 使传感器信号匹配现有PLC输入接法 |
| PLC | 完成启停、自保持、3秒准备和条件判断 |
| 光耦隔离模块 | 将PLC侧信号隔离后传给Arduino |
| Arduino | 读取许可信号,控制LED显示 |
控制权仍然在PLC。Arduino不另做一套启动、延时或传感器判断,只负责接收PLC的最终许可并显示。
三、实际信号链路
本次验证后的输出地址是Q1.5。
接近开关
↓
NPN转PNP转换器
↓
PLC物体检测输入
↓
启动保持+准备完成+工件到位
↓
Q1.5
↓
隔离模块输入侧
↓
隔离模块O1 → Arduino D4
↓
Arduino D8 → LED
本机使用的对应关系:
| 项目 | 地址/引脚 |
|---|---|
| PLC启动保持 | M10.0 |
| 3秒准备定时器 | T37 |
| 准备完成状态 | M10.2 |
| PLC到Arduino的输出 | Q1.5 |
| 隔离模块输出接Arduino | D4 |
| Arduino LED控制 | D8 |
传感器的PLC输入地址应以实际接线和在线监控确认结果为准,不在这里把端子编号直接当成程序地址。
接线时,PLC侧24V不能直接进入Arduino数字引脚。光耦模块的输入电压、输出侧供电和端子定义,也需要按实际模块规格确认。
四、PLC逻辑:在原输出前增加一个条件
前面已经验证的启停和3秒延时网络保持不变。本篇的主要变化,是在最终输出前增加工件检测条件。
文字梯形图:
M10.0 T37 工件到位
───| |────────────| |────────────| |────────( Q1.5 )
已启动 准备完成 检测有效 Arduino许可
对应逻辑:
Arduino许可
= 已启动
AND 3秒准备完成
AND 工件到位

这里没有对Q1.5做自保持。因此,准备完成后,工件一旦移走,检测条件不再成立,Q1.5就断开。
要注意区分两个状态:
- 准备完成:表示3秒准备过程已经结束。
- 允许执行:还需要工件到位,才给Arduino发送许可。
所以,PLC侧表示“准备完成”的绿灯可以保持亮着,而Arduino的LED仍然随着工件有无变化,两者含义不同。
五、Arduino:D4读取,D8显示
本次Arduino侧的引脚固定为:
const byte signalPin = 4;
const byte ledPin = 8;
// 隔离模块 O1 → Arduino D4
// LED控制端 → Arduino D8
const byte signalPin = 4;
const byte ledPin = 3;
// 隔离模块动作时,D4的有效电平。
// 先用LOW;若实测亮灭相反,再改为HIGH。
const byte signalActiveLevel = LOW;
// LED点亮所需电平。
// 普通接法:D8 → 限流电阻 → LED → GND,使用HIGH。
// 若为低电平点亮的LED模块,改为LOW。
const byte ledOnLevel = HIGH;
int lastSignal = -1;
void setup() {
Serial.begin(9600);
pinMode(signalPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
// 初始关闭LED
digitalWrite(ledPin, ledOnLevel == HIGH ? LOW : HIGH);
Serial.println("Test ready: input D4, LED D8");
}
void loop() {
int signal = digitalRead(signalPin);
bool active = (signal == signalActiveLevel);
// LED跟随输入是否有效
digitalWrite(
ledPin,
active ? ledOnLevel : (ledOnLevel == HIGH ? LOW : HIGH)
);
// 只在输入变化时打印
if (signal != lastSignal) {
Serial.print("D4 = ");
Serial.print(signal);
Serial.print(" | LED command = ");
Serial.println(active ? "ON" : "OFF");
lastSignal = signal;
}
delay(20);
}程序思路很简单:
读取D4
↓
判断隔离输出是否有效
↓
控制D8上的LED
隔离模块可能是低电平有效,也可能采用其他输出方式,需要根据实际模块和读取结果确认,不能简单认为“读到HIGH一定代表PLC接通”。
调试时,可以通过串口查看D4读数:
金属移开 → 记录D4状态
金属靠近 → 记录D4状态
先确认输入是否变化,再判断LED控制逻辑,不要只靠灯的亮灭推测信号有没有到达。
六、本次踩坑:软件ON,不等于实物输出已经动作
这一篇最花时间的,不是增加检测触点,而是核对输出地址。
1. 检测对象用错了
一开始拿非金属物体测试,没有得到预期反应。
后来用金属靠近,传感器才触发。这次使用的是金属检测用接近开关,不能把它当成漫反射光电传感器使用。
这也提醒我:先确认传感器检测什么,再排查接线和程序。
2. 输出公共端没有有效供电
排查时发现:
L+有24V
4L没有24V
核对并接好L+到4L后,4L恢复24V。但隔离模块仍未得到预期信号,说明问题还没有完全解决。
3. 最终根因是程序地址与物理端子不对应
当时程序写的是:
Q4.5
在线监控显示ON,但实际接线位置是:
DQb区5号端子
本次CR40上的这个端子对应:
Q1.5
把输出线圈从Q4.5改成Q1.5后,问题解决。这次最值得记住的一点是:
在线监控中的输出位为ON,只说明程序把该地址置成了1;还必须确认它对应的物理端子、公共端供电和外部回路。
4. 调试时不要让输入、输出共用一个引脚
Arduino调试中,还曾误把两个引脚都设为D4:
const byte signalPin = 4;
const byte ledPin = 4;这会让读取输入与驱动输出发生冲突。本次实际接线是D4输入、D8输出,两者必须分开。
七、第五课验收表
本课按以下现象检查:
| 测试操作 | 预期结果 |
|---|---|
| 未启动,金属靠近 | Q1.5不接通,Arduino LED灭 |
| 启动后未满3秒,金属靠近 | 仍不允许输出 |
| 准备完成,没有金属 | Q1.5断开,Arduino LED灭 |
| 准备完成,金属靠近 | Q1.5接通,Arduino LED亮 |
| 移走金属 | Q1.5断开,Arduino LED灭 |
| 金属仍在,按停止 | 撤销许可,Arduino LED灭 |
拍摄时,把在线监控、接近开关和Arduino LED一起展示,会更容易看清“条件变化—PLC输出—实物显示”的对应关系。
八、下一篇
第五课解决的是:
工件没到,不允许执行。
下一篇准备增加一个等待期限:
准备完成
↓
开始等待工件
├─ 规定时间内到位 → 允许运行
└─ 超时仍未到位 → 报警先把现场输入读对、输出地址对应好,再往上增加流程逻辑。这次绕了不少路,但也留下了一条很有价值的排障记录。





