はじめに
本来ならここで私がPLCとは何かから,通信の設定方法は~などと説明していかなけれなならないのでしょうが,私が1から書くよりもはるかにわかりやすくかいてくださっている記事PLCとRaspberry Piはとっても相性がいい。(通信の行い方)があるので参考にしてください.
なお本記事は上記で紹介した記事のセットアップは済んでいるものとして進めていきます.
(@Tatsuya-8888様リスペクトです。)
組み込みならばc言語で
PLCとRaspberry Piはとっても相性がいい。(通信の行い方)
でもそうですが,MCプロトコルをpythonをつかって実装している記事はしらべると見つかります.しかしC言語での実装例は簡単にはみつからなかったので今回はC言語で実装してみます.
cな理由は私がc言語しか理解できないからです.
#環境
MELSEC Q03UDVCPU
RaspberryPi3B+
OS:Raspbian
シーケンサのIP:192.168.0.71
port:5015
ラズパイ上での作業がスムーズになるようにVNC Viewerからラズパイを操作しましょう.
導入方法はRaspberry Pi へのリモート接続がわかりやすいです.(なげやり)
ソース
PLCとRaspberry Piはとっても相性がいい。(通信の行い方)
と同じコマンドのほうがわかりやすいと思いますので,
命令はワード単位(一括)読み出しです.
今回はD1を読み取ります.
5000 00 FF 03FF 00 0018 0020 0401 0000 D*000001 0001
#include <stdio.h> //printf(), fprintf(), perror()
#include <sys/socket.h> //socket(), bind(), accept(), listen()
#include <arpa/inet.h> // struct sockaddr_in, struct sockaddr, inet_ntoa(), inet_aton()
#include <stdlib.h> //atoi(), exit(), EXIT_FAILURE, EXIT_SUCCESS
#include <string.h> //memset(), strcmp()
#include <unistd.h> //close()
#define MSGSIZE 1024
#define BUFSIZE (MSGSIZE + 1)
int server_open(const char *address, unsigned short port) {
int server;
struct sockaddr_in sockaddr;
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
if (inet_aton(address, &sockaddr.sin_addr) == 0) {
fprintf(stderr, "Invalid IP Address.\n");
exit(EXIT_FAILURE);
}
if (port == 0) {
fprintf(stderr, "invalid port number.\n");
exit(EXIT_FAILURE);
}
sockaddr.sin_port = htons(port);
if ((server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("socket() failed.");
exit(EXIT_FAILURE);
}
if (connect(server, (struct sockaddr*) &sockaddr, sizeof(sockaddr)) < 0) {
perror("connect() failed.");
exit(EXIT_FAILURE);
}
printf("connect to %s\n", inet_ntoa(sockaddr.sin_addr));
return server;
}
void server_send(int server, const char *data, int size) {
if (send(server, data, size, 0) <= 0) {
perror("send() failed.");
exit(EXIT_FAILURE);
}
}
int server_receive(int server, char *buffer) {
int totalSize = 0;
int receivedSize = recv(server, &buffer[totalSize], 100, 0);
if (receivedSize > 0) {
return receivedSize;
}
else if (receivedSize == 0) {
perror("ERR_EMPTY_RESPONSE");
exit(EXIT_FAILURE);
}
else {
perror("recv() failed.");
exit(EXIT_FAILURE);
}
}
void server_close(int server) {
if (close(server) == 0) {
perror("close() failed.");
exit(EXIT_FAILURE);
}
}
int request(char *buffer, int size) {
int i;
char cmd[BUFSIZE] = "500000FF03FF000018002004010000D*0000010001";
for (i = 0; i<strlen(cmd); i++) {
buffer[i] = cmd[i];
}
return strlen(buffer);
}
int main(int argc, char* argv[]) {
const char *address = "192.168.0.71";
unsigned short port = (unsigned short)atoi("5015");
int server = server_open(address, port);
char send_buffer[BUFSIZE];
char recv_buffer[BUFSIZE];
int size = request(send_buffer, BUFSIZE);
printf("%s\n", send_buffer);
server_send(server, send_buffer, size);
size = server_receive(server, recv_buffer);
printf("%s\n", recv_buffer);
}
ソケット通信のプログラムはC言語で学ぶソケットAPI入門 第1回 サーバ編を参考にさせていただきました.
ちなみに以下の部分を
const char *address = "192.168.0.71";
unsigned short port = (unsigned short)atoi("5015");
このように変更すると
if (argc != 3) {
fprintf(stderr, "argument count mismatch error.\n");
exit(EXIT_FAILURE);
}
const char *address = argv[1];
unsigned short port = (unsigned short)atoi(argv[2]);
コマンドラインから./実行ファイル IP portと指定して実行できます.
そして今回のミソとなる部分は
int request(char *buffer, int size) {
int i;
char cmd[BUFSIZE] = "500000FF03FF000018002004010000D*0000010001";
for (i = 0; i<strlen(cmd); i++) {
buffer[i] = cmd[i];
}
return strlen(buffer);
}
かなりジジくさいプログラムになっています.
配列にコマンドをそのまま打ち込んだものです.
実行結果
シーケンサ側D1にDATERDを使って2019を書き込みます.
2019が送られてきました.
次回
request関数にジジくささが残るのでよりつかいやすくなるよう改良します.
PLCとRaspberryPi間をC言語でソケット通信 MCプロトコル編2
おまけ
弊社のご紹介になります。
生産設備向けのPLC、PCソフトの設計・開発を行っております。
未経験からの採用もしております。
弊社メンバーは学習した内容を、日々Qiitaの記事にアウトプットしております。
弊社にご興味のある方、まずはLINEで気軽に問い合わせしてみてください!
ご応募お待ちしておりますー!