長岡ものづくりアカデミー
• USB接続ができるWindowsマシンが必要です。
• インターネットに接続可能な環境が必要です。
•
ArduinoのIDEをインストールしておいてください。
https://www.arduino.cc/
• その他 可能な方はIDEに追加インストールをお願いします。
• ESP32系のCPUを使います。
• DHTライブラリーを使います。
• USBシリアルドライバーにはCH430チップを使います。
旭鉄工株式会社のDX事例を見てみましょう。
「生産管理板を書いてPDCAを回しなさい!」
人にはもっと付加価値の高い仕事を・・
1)大掛かりで高い
2)昭和の機器にはつかない
3)欲しいデータが見えない
1. 稼働率モニター(第一世代)
(ア) シグナルタワーの読み取り
(イ) スマートあんどん
2. サイクルタイムモニター(第二世代)
(ア) 生産数カウンター
• スイッチ
• 近接センサー(光)
• リードスイッチ(磁石)
• ホールセンサー(磁気)
赤外線を出し、その反射光があるかないかを読み取る
型名 E3F-DS10 P1
駆動電圧 5-36V DC
検出距離 10cm以内
入力モード PNP
検出タイプ NO
センサーのNOタイプとNCタイプの違い?NOタイプは検出物体がある時にON。NCタイプはない時にONします。
PNP型トランジスタは、ヨーロッパなどを中心に多用。
PNP型トランジスタを用いた回路は、負荷電流が流れだす(ソース)側となるので、回路のGND(0V)とコレクタの間に負荷を接続します。
PNPオープンコレクタ出力の場合、エミッタが電源の+側と接続されており、0Vと出力の間に負荷を接続することによって動作させることができます。
トランジスタがオンになると、出力端子の電圧は電源の+側と同じになります。
電流は、トランジスタのエミッター(E端子)からコレクタ(C端子)に流れたいのですが、流れることができません。これは、ベース(B端子)に電流が流れていない状態だからです。
すると主回路からトランジスタのB端子(ベース)に電流が流れます。
B端子に電流が流れると、端子Eと端子Cの間に電流がながれるようになります。
このとき、負荷Aには、電流が流れているので回路に流れている電圧と同じ電圧がかかります。
負荷Aの値をマイコンで読み込むと光電センサーの前にカウントしたい製品があるかどうかがわかります。
光電センサーの前には、製品があるときに電圧がHiになる。製品と製品との間では、電圧はLoになる。こういったアナログ電圧の変化をマイコンは読み取ります。マイコンを使って10ms間隔で光電センサーの出力信号を読込み続けます。マイコンは命令された通り、10ms間隔データを読み取ると、センサーの前に物体があるときはHiの信号を出し続けます。また、センサーの前に物体がなくなるとLo信号を出し続けます。これから個数(カウント)へと変換します。
アルゴリズム
1)前の状態から変化があった。
2)現在の状態がONである
1)と2)が成り立つときに、カウントを+1する。これから個数(カウント)へと変換します。
光電センサーを駆動するには電源が必要です。LED等であれば、Arduinoなどのマイコンであればギリギリ駆動できます。Arduino等はマイコンの種類によりますが5mA程度のLEDを駆動するだけで、マイコン的には限界だと思います。これは、最近のマイコンは低電力が求められているためです。5mA程度のLEDを駆動する場合には、1kΩの電流制限用抵抗をいれる必要があります。
今回使うマイコンボードでは、ACアダプターから9Vが入力され、電源回路によって3.3Vと5Vの出力がボード上から得ることができる。光電センサーの駆動電圧は5V―36Vなのでボード上の5V出力端子にそのまま接続しても大丈夫。
• D1R32ボードはUSB通信用にCH430チップを搭載
• 検索で「install CH430 drivers Windows」と打ってドライバーをインストール先を探します。
• sparksにあったのでダウンロードしてインストールする。
https://sparks.gogo.co.nz/ch340.html
• 【注意】USBドライバーが新しすぎると動かないかも・・・
• 2014.8.8日製のUSBドライバーでの動作を確認しました。最新のは動きがおかしかったです。。。
ArduinoIDEではいろいろなマイコンを開発することができます。しかし、そのためにはボードのサポートライブラリーをインストールする必要があります。
• ファイルー>環境設定ー>設定のtabから
• 追加のボードマネージャーURLとして以下を追記します。
https://espressif.github.io/arduino-esp32/package_esp32_index.json
• ツールー>ボードー>ボードマネージャーから
• ESP32 をインストールします。
• ツールー>ボード>ESP32 Arduinoから
• ESP32 Wrover Modulesを選択します。
• ツールー>ライブラリーの管理 から
• DHTで検索し DHT sensor library を選択します。
void setup() {
Serial.begin(115200);
}
void loop() {
Serial.println("Hello World");
}
#include <DHT.h>
//ボードに接続したDHTセンサーのピン番号IO17 (GPIO)
const int PIN_DHT = 17;
DHT dht(PIN_DHT,DHT11);
// 温度を取得
String getTemperature() {
float temperature = dht.readTemperature();
return String(temperature);
}
// 湿度を取得
String getHumidity() {
float humidity = dht.readHumidity();
return String(humidity);
}
void setup() {
Serial.begin(115200);
// dhtライブラリの初期処理
dht.begin();
}
void loop() {
Serial.print("温度:" + getTemperature());
Serial.println("湿度:" + getHumidity());
sleep(2);//2秒まつ
}
#include <DHT.h>
//DHTセンサーのピン番号IO17 (GPIO)
const int PIN_DHT = 17;
DHT dht(PIN_DHT,DHT11);
//PESセンサーのピン番号IO16 (GPIO)
const int pesPin = 16;
// 温度を取得
String getTemperature() {
float temperature = dht.readTemperature();
return String(temperature);
}
// 湿度を取得
String getHumidity() {
float humidity = dht.readHumidity();
return String(humidity);
}
void setup() {
Serial.begin(115200);
pinMode(pesPin, INPUT);
// dhtライブラリの初期処理
dht.begin();
}
unsigned long count;
int state = 0;
int previous = 0;
void loop() {
state = digitalRead(pesPin);
if( state != previous && state == 1 ){
count++;
Serial.print("count :");
Serial.println(count);
Serial.print("温度:" + getTemperature());
Serial.println("湿度:" + getHumidity());
}
previous = state;
}
ESP32のWi-Fi設定をsoftAPモードに設定し、Webサーバーを動かします。
サーバーの構築には下記のファイルが必要です。
Arduino IDEに、必要なライブラリをインストールしていきます。
最初にWebサーバーを動かすための「ESPAsyncWebServer」をインストールします。
Githubページ(https://github.com/me-no-dev/ESPAsyncWebServer)から
zipファイルをダウンロードします。
Githubページ上の右上にあるCODEをクリック
Download ZIPでzipファイルをダウンロードします。
ArduinoIDEのメニューから スケッチ ー> ライブラリーをインクルード ー> .ZIP形式のライブラリーをインクルード
を選択し、さきほどダウンロードしたESPAsyncWebServer-master.zipファイルを
選択して「開く」ことでESPAsyncWebServerがArduinoのライブラリーにインクルードされます。
次に、ESPAsyncWebServerが依存している「AsyncTCP」というライブラリもインストールします。
Githubページ(https://github.com/me-no-dev/AsyncTCP)から
zipファイルをダウンロードします。
Githubページ上の右上にあるCODEをクリック
Download ZIPでzipファイルをダウンロードします。
ArduinoIDEのメニューから スケッチ ー> ライブラリーをインクルード ー> .ZIP形式のライブラリーをインクルード
を選択し、さきほどダウンロードしたAsyncTCP-master.zipファイルを
選択して「開く」ことでAsyncTCPがArduinoのライブラリーにインクルードされます。
サーバーの構築には下記のファイルが必要です。
index.html <=アクセスした際に最初に見えるWEBページ HTML言語
style.ccs <=WEBページのデザイン
これらのファイルは、ESP32のSPIFFS (SPI Flash File System) に保存して
おくと便利です。
ESP32のフラッシュメモリの一部をストレージとして使う方法をSPIFFS
と言います。
SPIFFSを使うためにはArduinoのIDEに追加のアップローダをインストール
して機能を追加する必要があります。
google「ESP32 SPIFFS 」といれて検索するか以下のサイトからもらってきます
https://github.com/me-no-dev/arduino-esp32fs-plugin/releases/tag/1.1
展開すると
ESP32FS\tool\esp32fs.jar
となっている。
Arduinoのtoolsに先ほど展開した
ESP32FS\tool\esp32fs.jar
をコピーすれば良い。
スケッチがあるフォルダにアップローダ用のフォルダ
を作ります。
名前は 「data」 と必ずしてください。
dataフォルダーにindex.htmlとstyle.cssを配置
します。
Arduinoの「ツール」からSPIFFSに割り当てるサイズを選びます。
ESP32は4Mのメモリを持っています。
今回は下記とします。
プログラム 1.2MByte
SPIFFS 1.5MByte
を割り当てます。
/**********************************************
ESP32 ながおかアカデミー アクセスポイントバージョン
Ver. 20230904
**********************************************/
// Import required libraries
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include "FS.h"
#include "SPIFFS.h"
#include <DHT.h>
#include <DHT_U.h>
TaskHandle_t th[2];
//ボードに接続したDHTセンサーのピン番号IO17 (GPIO)
const int PIN_DHT = 17;
DHT dht(PIN_DHT,DHT11);
//ボードに接続したPESセンサーのピン番号IO16 (GPIO)
const int pesPin = 16;
String pesState;
//ボード上に実装されているLEDのピン番号IO2 (GPIO)
const int ledPin = 2;
String ledState;
String ledState_old = "OFF";
// アクセスポイントとパスワード
const char* ssid = "ESP32AP";
const char* pass = "12345678";
const IPAddress ip(192,168,4,1);
const IPAddress subnet(255,255,255,0);
AsyncWebServer server(80);
bool myWiFiFirstConnect = true;
// Wifi接続用タスク
void myWiFiTask(void *pvParameters) {
while (true) {
IPAddress myIP = WiFi.softAPIP();
if (!myIP) {
Serial.println("Connecting WiFi");
WiFi.softAP(ssid, pass); // SSIDとパスの設定
delay(100); // このdelayを入れないと失敗する場合がある
WiFi.setTxPower(WIFI_POWER_19_5dBm);//電波強度を最大
WiFi.softAPConfig(ip, ip, subnet); // IPアドレス、ゲートウェイ、サブネットマスクの設定
IPAddress myIP = WiFi.softAPIP();
// 各種情報を表示
Serial.print("SSID: ");
Serial.println(ssid);
Serial.print("AP IP address: ");
Serial.println(myIP);
}
vTaskDelay (5000); // Check again in about 5s
}
}
// Sensorカウントタスク
unsigned long count;
void mySensorTask(void *pvParameters) {
count = 0;
int state = 0;
int previous = 0;
while(1){
state = digitalRead(pesPin);
if( state != previous && state == 1 && ledState.equals("ON") ){
count++;
Serial.print("count :");
Serial.println(count);
}
previous = state;
vTaskDelay (10);
}
}
String getState() {
// LEDの状態を取得
if(digitalRead(ledPin)){
ledState = "ON";
} else {
ledState = "OFF";
}
return String(ledState) ;
}
String getTemperature() {
// 温度を取得
float temperature = dht.readTemperature();
return String(temperature);
}
String getHumidity() {
// 湿度を取得
float humidity = dht.readHumidity();
return String(humidity);
}
String getPESensor() {
return String(count);
}
String getState() {
// LEDの状態を取得
if(digitalRead(ledPin)){
ledState = "ON";
} else {
ledState = "OFF";
}
return String(ledState) ;
}
String getTemperature() {
// 温度を取得
float temperature = dht.readTemperature();
return String(temperature);
}
String getHumidity() {
// 湿度を取得
float humidity = dht.readHumidity();
return String(humidity);
}
String getPESensor() {
return String(count);
}
unsigned long time_data = 0;
unsigned long time_start = 0;
unsigned long second = 0;
String getTimer() {
// 経過時刻を取得
time_data = millis();
if(ledState.equals("ON")){
second = time_data/1000 - time_start/1000 ;
} else {
second = 0;
}
return String(second);
}
String getAverage() {
// 一秒あたりの平均個数を取得
float average = 0.0 ;
if( second && count ){
average = second / (float)count ;
//Serial.println(average);
}
return String(average,3);
}
// LEDの状態と温湿度の更新
String processor(const String& var){
if(var == "STATE"){
if(digitalRead(ledPin)){
ledState = "ON";
if(!ledState.equals(ledState_old)) {
count = 0;
}
} else {
ledState = "OFF";
time_start = millis();
}
ledState_old = ledState;
return ledState;
} else if (var == "TEMPERATURE"){
return getTemperature();
} else if (var == "HUMIDITY"){
return getHumidity();
} else if (var == "PESENSOR"){
return getPESensor();
} else if (var == "TIMER"){
return getTimer();
} else if (var == "AVERAGE"){
return getAverage();
}
return String();
}
#include "nvs_flash.h"
void setup(){
Serial.begin(115200);
//Serial.setDebugOutput(true);
Serial.println("NVM ERROR CHECK");
ESP_ERROR_CHECK(nvs_flash_erase());
nvs_flash_init();
// dhtライブラリの初期処理
dht.begin();
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
pinMode(pesPin, INPUT);
// Initialize SPIFFS
if(!SPIFFS.begin(true)){
Serial.println("An Error has occurred while mounting SPIFFS");
return;
}
// タスクの作製 コア0にて動作 サイズ8kB
xTaskCreatePinnedToCore(myWiFiTask, "myWiFiTask", 8192, NULL, 3, &th[0], 0);
// タスクの作製 コア1にて動作 サイズ8kB
xTaskCreatePinnedToCore(mySensorTask, "mySensorTask", 4096, NULL, 5,&th[1], 1);
// ルートアクセス [/] web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// style.css
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/style.css", "text/css");
});
// GPIOをONにする HIGH
server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
digitalWrite(ledPin, HIGH);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// GPIOをOFFにする LOW
server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){
digitalWrite(ledPin, LOW);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// LED状態表示
server.on("/state", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", getState().c_str());
});
// 温度表示
server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", getTemperature().c_str());
});
// 湿度表示
server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", getHumidity().c_str());
});
// 光電センサー表示
server.on("/pesensor", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", getPESensor().c_str());
});
// 経過時間表示
server.on("/timer", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", getTimer().c_str());
});
// 一個あたりの平均経過時間
server.on("/average", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/plain", getAverage().c_str());
});
server.begin();
}
void loop(){
}
<!DOCTYPE html>
<!--
Shouhei Yano
National Institute of Technology College, in Nagaoka .
-->
<html lang="ja">
<head>
<meta charSet="utf-8"/>
<title>NagaokaアカデミーWEBサーバ</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<h1>Nagaokaアカデミー</h1>
<p>
<span class="sensor-labels">温度</span>
<span id="temperature">%TEMPERATURE%</span>
<sup class="units">°C</sup>
</p>
<p>
<span class="sensor-labels">湿度</span>
<span id="humidity">%HUMIDITY%</span>
<sup class="units">%</sup>
</p>
<p>
<span class="sensor-labels">現在の個数</span>
<span id="pesensor">%PESENSOR%</span>
<sup class="units">個</sup>
</p>
<p>
<span class="sensor-labels">経過時間 </spin>
<span id="timer">%TIMER%</span>
<sup class="units">秒</sup>
</p>
<p>
<span class="sensor-labels">一個当たりの時間 </spin>
<span id="average">%AVERAGE%</span>
<sup class="units">秒/個</sup>
</p>
<p>カウント<strong><span id="state">%STATE%</span></strong> </p>
<p>
<a href="/on"><button class="button">ON</button></a>
<a href="/off"><button class="button button2">OFF</button></a>
</p>
</body>
<script>
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("state").innerHTML = this.responseText;
}
};
xhttp.open("GET", "/state", true);
xhttp.send();
}, 10000 ) ;
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("temperature").innerHTML = this.responseText;
}
};
xhttp.open("GET", "/temperature", true);
xhttp.send();
}, 10000 ) ;
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("humidity").innerHTML = this.responseText;
}
};
xhttp.open("GET", "/humidity", true);
xhttp.send();
}, 10000 ) ;
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("pesensor").innerHTML = this.responseText;
}
};
xhttp.open("GET", "/pesensor", true);
xhttp.send();
}, 1000 ) ;
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("timer").innerHTML = this.responseText;
}
};
xhttp.open("GET", "/timer", true);
xhttp.send();
}, 1000 ) ;
setInterval(function ( ) {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("average").innerHTML = this.responseText;
}
};
xhttp.open("GET", "/average", true);
xhttp.send();
}, 1000 ) ;
</script>
</html>
/***
Rui Santos
Complete project details at https://RandomNerdTutorials.com
***/
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h1 {
color: #0F3376;
padding: 2vh;
}
p {
font-size: 1.5rem;
}
.button {
display: inline-block;
background-color: #008CBA;
border: none;
border-radius: 4px;
color: white;
padding: 16px 40px;
text-decoration: none;
font-size: 30px;
margin: 2px;
cursor: pointer;
}
.button2 {
background-color: #f44336;
}
.units {
font-size: 1.2rem;
}
.sensor-labels {
font-size: 1.5rem;
vertical-align:middle;
padding-bottom: 15px;
}