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

# はじめに
本来ならここで私がPLCとは何かから，通信の設定方法は～などと説明していかなけれなならないのでしょうが，私が1から書くよりもはるかにわかりやすくかいてくださっている記事[PLCとRaspberry Piはとっても相性がいい。(通信の行い方)](https://qiita.com/Tatsuya-8888/items/28cc12dd637c0866fd1c)があるので参考にしてください．
なお本記事は上記で紹介した記事のセットアップは済んでいるものとして進めていきます．
(@Tatsuya-8888様リスペクトです。)

# 組み込みならばc言語で
[PLCとRaspberry Piはとっても相性がいい。(通信の行い方)](https://qiita.com/Tatsuya-8888/items/28cc12dd637c0866fd1c)
でもそうですが，MCプロトコルをpythonをつかって実装している記事はしらべると見つかります．しかしC言語での実装例は簡単にはみつからなかったので今回はC言語で実装してみます．
cな理由は私がc言語しか理解できないからです．

#環境
MELSEC Q03UDVCPU
RaspberryPi3B+　
OS:Raspbian

シーケンサのIP:192.168.0.71
port:5015

ラズパイ上での作業がスムーズになるようにVNC Viewerからラズパイを操作しましょう．
導入方法は[Raspberry Pi へのリモート接続](https://qiita.com/b-wind/items/d33f759ace01658bb09e)がわかりやすいです．(なげやり)

# ソース
[PLCとRaspberry Piはとっても相性がいい。(通信の行い方)](https://qiita.com/Tatsuya-8888/items/28cc12dd637c0866fd1c)
と同じコマンドのほうがわかりやすいと思いますので，
命令はワード単位(一括)読み出しです．
今回はD1を読み取ります．

```
5000 00 FF 03FF 00 0018 0020 0401 0000 D*000001 0001
```

```c: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回 サーバ編](https://qiita.com/tajima_taso/items/2f0606db7764580cf295)を参考にさせていただきました．
ちなみに以下の部分を

```c


    const char *address = "192.168.0.71";
    unsigned short port = (unsigned short)atoi("5015");

```
このように変更すると

```c


    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と指定して実行できます．
そして今回のミソとなる部分は

```c
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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333756/5ba8e84f-f063-3567-5c0b-dfb7e0d9a804.png)

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

![D1_2019.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333756/aa08cb3e-ce3b-3ccc-e48d-4db73189c206.png)

書き込み後
![a.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333756/e19146af-ad47-1925-06ae-7d73813faaa4.png)

2019が送られてきました．

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

[PLCとRaspberryPi間をC言語でソケット通信　MCプロトコル編2](https://qiita.com/kiki0817/items/7d9c0a9307f76dcc6335)

# おまけ

弊社のご紹介になります。
生産設備向けのPLC､PCソフトの設計･開発を行っております｡
未経験からの採用もしております。
弊社メンバーは学習した内容を、日々Qiitaの記事にアウトプットしております。
弊社にご興味のある方、まずはLINEで気軽に問い合わせしてみてください！
ご応募お待ちしておりますー！

https://lin.ee/PEI7SJf

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/333756/6c44e226-e65f-fdc9-a8cf-54ac0bec504c.png)