LoginSignup
11
17

More than 3 years have passed since last update.

PLCとRaspberryPi間をC言語でソケット通信 MCプロトコル編1

Last updated at Posted at 2019-08-26

はじめに

本来ならここで私が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
D1_word_read.c
#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);
}

かなりジジくさいプログラムになっています.
配列にコマンドをそのまま打ち込んだものです.

実行結果

書き込み前
上が命令文
下が応答文です.
キャプチャ.PNG

シーケンサ側D1にDATERDを使って2019を書き込みます.

D1_2019.png

書き込み後
a.PNG

2019が送られてきました.

次回

request関数にジジくささが残るのでよりつかいやすくなるよう改良します.

PLCとRaspberryPi間をC言語でソケット通信 MCプロトコル編2

11
17
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
17