Arduino MKR1000 WiFi ピック・トゥ・ライトシステム - プロジェクト2
必要な部品と材料
| 部品 | 数量 | 備考 |
|---|---|---|
| Arduino MKR1000 | 1 | Wi-Fi内蔵、LiPo充電機能付き |
| LED(汎用) | 2 | ビン指示用(Bin1、Bin2) |
| ジャンパーワイヤー | 1セット | |
| タクトスイッチ 12mm | 1 | ピッキング完了確認用 |
| 抵抗 10kΩ | 1 | スイッチプルアップ用 |
| 抵抗 330Ω | 4 | LED電流制限、トランジスタベース用 |
| ブレッドボード | 1 | |
| USB-A - Micro USBケーブル | 1 | 電源・書き込み用 |
| 汎用NPNトランジスタ(2N2222等) | 1 | LED駆動用(Wi-Fi動作時の電圧降下対策) |
プロジェクト概要
倉庫や工場でのピッキング作業を支援する「ピック・トゥ・ライト」システムの第2弾です。前回はUSBシリアル通信でPCと接続していましたが、今回はMKR1000のWi-Fi機能を活用し、UDP(User Datagram Protocol)でワイヤレス化しました。さらに、ピッキング順序番号を確認応答に含めることで、作業者がどの順番まで完了したかを管理側で把握できるようにしています。Pythonスクリプト(PC側)がCSVシーケンスファイルを読み、順番にビン番号を送信。Arduino側で該当LEDを点灯、作業者がボタン押下で完了確認、順序番号を返送して次工程へ進みます。
セットアップ手順
ステップ1: Python 3.6以上のインストール
Python公式サイトからダウンロード・インストールしてください。
ステップ2: Pythonソケットライブラリ
標準ライブラリのsocketモジュールを使用するため、追加インストールは不要です。
ステップ3: Arduino IDEの準備
ステップ4: Python作業ディレクトリの確認
IDLEを起動し、以下を実行してカレントディレクトリを確認:
>>> import os
>>> os.getcwd()
ステップ5: シーケンスCSVファイルの作成
メモ帳で以下の内容のファイルを作成し、ステップ4で確認したディレクトリにsequence1.txtとして保存:
0001,Bin1
0002,Bin2
0003,Bin1
0004,Bin2
0005,Bin1
0006,Bin2
0007,Bin1
0008,Bin2
0009,Bin1
0010,Bin2
0011,Bin1
0012,Bin2
0013,Bin1
4桁の順序番号とビン番号をカンマ区切りで記述します。
ステップ6: Arduinoシリアルモニタ確認用
スケッチ内でSerial.printを使用し、動作確認用メッセージを出力しています。
ステップ7: Pythonスクリプトの作成
IDLEで新規ファイルを作成し、後述のPythonスクリプトを貼り付け保存。CSVを1行ずつ読み、UDPで順序番号+ビン番号を送信、Arduinoからの応答(順序番号)を検証し、不一致ならエラー停止します。
ステップ8: Arduinoスケッチの書き込み
- Webエディタの場合: 新規スケッチに貼り付けると
arduino_secrets.hタブが自動生成されるので、SSIDとパスワードを記入 - デスクトップIDEの場合: ファイル→スケッチ例→WiFi101→WiFiUdpSendReceiveStringを開き、同梱の
arduino_secrets.hに認証情報を記入してからスケッチを上書き - ライブラリ
WiFi101が必要(ライブラリマネージャからインストール) - MKR1000へ書き込み
ステップ9: 配線(重要:トランジスタ使用)
MKR1000のWi-Fi動作開始時にピン8・9の電圧が低下しLEDが点灯しなくなる現象があるため、NPNトランジスタでLEDを駆動する回路に変更しています。フリッツング図を参照し、以下の点に注意して配線してください:
- LEDとトランジスタに適切な抵抗値を使用
- 5Vがピン8・9間でショートしないよう注意(ダイオード併用推奨)
- 配線間違いでMKR1000が破損する恐れがあります
ステップ10: 動作確認
- ArduinoがWi-Fi接続完了(シリアルモニタで「Connected to wifi」表示)を待つ
- PythonスクリプトをF5で実行
- Bin1のLEDが点灯 → ボタン押下 → 順序番号返送 → 次のBin2へ…と進行
- シーケンスエラー検知テスト: スケッチ内の
myseq.toCharArray(ReplyBuffer, 5);をコメントアウトし、char ReplyBuffer[5] = "0001";を有効化すると、2回目のBinでエラー停止することを確認可能
Arduinoスケッチ
#include <SPI.h>
#include <WiFi101.h>
#include <WiFiUdp.h>
const int OKbutton = 2; // 完了確認ボタン
const int Bin1 = 8; // Bin1 LED(トランジスタ経由)
const int Bin2 = 9; // Bin2 LED(トランジスタ経由)
String mydata = "";
String myseq = "";
int buttonPress = 0;
int status = WL_IDLE_STATUS;
#include "arduino_secrets.h"
char ssid[] = SECRET_SSID;
char pass[] = SECRET_PASS;
int keyIndex = 0;
unsigned int localPort = 2390;
char packetBuffer[255];
char ReplyBuffer[5] = "";
String mystring;
WiFiUDP Udp;
void setup() {
Serial.begin(9600);
while (!Serial) {}
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
while (true);
}
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
delay(10000);
}
Serial.println("Connected to wifi");
printWiFiStatus();
Serial.println("\nStarting connection to server...");
Udp.begin(localPort);
}
void loop() {
int packetSize = Udp.parsePacket();
if (packetSize) {
Serial.print("Received packet of size ");
Serial.println(packetSize);
Serial.print("From ");
IPAddress remoteIp = Udp.remoteIP();
Serial.print(remoteIp);
Serial.print(", port ");
Serial.println(Udp.remotePort());
int len = Udp.read(packetBuffer, 255);
if (len > 0) packetBuffer[len] = 0;
Serial.println("Contents:");
Serial.println(packetBuffer);
String mystring(packetBuffer);
mydata = mystring.substring(4); // ビン番号部分
myseq = mystring.substring(0, 4); // 順序番号部分
Serial.println(mydata);
Serial.println(myseq);
while (mydata != "") {
if (mydata == "Bin1") {
digitalWrite(Bin1, HIGH);
digitalWrite(Bin2, LOW);
while (buttonPress != HIGH) {
buttonPress = digitalRead(OKbutton);
mydata = "";
}
digitalWrite(Bin1, LOW);
Serial.println("Picked");
buttonPress = 0;
delay(1000);
}
if (mydata == "Bin2") {
digitalWrite(Bin2, HIGH);
digitalWrite(Bin1, LOW);
while (buttonPress != HIGH) {
buttonPress = digitalRead(OKbutton);
mydata = "";
}
digitalWrite(Bin2, LOW);
Serial.println("Picked");
buttonPress = 0;
delay(1000);
}
}
myseq.toCharArray(ReplyBuffer, 5);
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(ReplyBuffer);
Udp.endPacket();
Serial.println(ReplyBuffer);
}
}
void printWiFiStatus() {
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
Pythonスクリプト
import csv
import socket
import time
import sys
UDP_IP = "192.168.1.119" # ArduinoのIPアドレスに変更
UDP_PORT = 2390
print("UDP target IP:", UDP_IP)
print("UDP target port:", UDP_PORT)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
time.sleep(5)
with open('sequence1.txt') as csvDataFile:
csvReader = csv.reader(csvDataFile)
for row in csvReader:
myseq = row[0]
mystate = row[1]
myrow = row[0] + row[1]
print("Current sequence being picked", myseq, "from", mystate)
sock.sendto(bytes(myrow, "utf-8"), (UDP_IP, UDP_PORT))
data = ""
while data == "":
(data, addr) = sock.recvfrom(1024)
mytest = data.decode("utf-8")
print("picked =", mytest)
if mytest != myseq:
print("there is a sequence fault at sequence", mytest)
sys.exit()
シーケンスファイル(sequence1.txt)
0001,Bin1
0002,Bin2
0003,Bin1
0004,Bin2
0005,Bin1
0006,Bin2
0007,Bin1
0008,Bin2
0009,Bin1
0010,Bin2
0011,Bin1
0012,Bin2
0013,Bin1
結論と今後の展望
ワイヤレス化とシーケンス管理機能を実装し、実用的なピック・トゥ・ライトシステムの基盤が完成しました。シンプルな構成ながら拡張性が高く、ビン数増加やディスプレイ表示、バーコードリーダー連携などへの発展が容易です。プロジェクト3では、さらなる信頼性向上(再送制御、ハートビート監視)や、複数Arduinoの協調動作、Webダッシュボードによる進捗可視化などに挑戦予定です。
参考リンク
- Fritzing回路図:
picktolightudp_OCbpNt9XPK.fzz - Arduino WiFi101ライブラリ: 公式ガイド
製造プロセス