Header Ads

Arduino温泉卵機の製作9(ソフトウェア製造)

どうも、とらです(๑´∀`) 前回のハードウェア製造に引き続き、Arduino温泉卵機のソフトウェア製造を行うことにしました。








Arduino制御


PIDライブラリ
に付属していたサンプルプログラムに、ちょこちょこ肉付けして作りました。使用したのはPID_Basicdではなく、PID_RelayOutputです。

PIDを邪魔することなく1秒おきに温度データをシリアル通信させる為に、delay(1000)ではなくmillis()を使いました。やり方はBlink Without Delayが参考になります。これでEXPID(実験ID)、Input(熱電対で測定した温度)、Output(PID出力)が1秒ごとにArduinoから送信されてきます。でも実際はArduinoの中でもっと高い頻度でPID計算がされ、OutputがSSRに送られています。

実験IDはあとでDBからデータを取るのに楽をしたいから定義しました。また、温泉卵を作る温度は67℃としました。たまに熱電対の温度データがnanになってしまうことがあったので、その処理も加えました。







#include <SPI.h>
#include <PID_v1.h>
#include "Adafruit_MAX31855.h"

#define EXPID 1
#define SETPOINT 67
#define DO  10
#define CS  9
#define CLK 8
#define SSR 5

Adafruit_MAX31855 thermocouple(CLK, CS, DO);
unsigned long previousMillis = 0;
const long interval = 1000;           // interval (ms) at which Arduino sends temperature data

//PID
double Setpoint, Input, Output;
PID myPID(&Input, &Output, &Setpoint, 2, 5, 1, DIRECT);
int WindowSize = 5000;
unsigned long windowStartTime;

void setup() {
  pinMode(SSR, OUTPUT);
  Serial.begin(9600);

  Serial.println("Expid,Input,Output");
  // wait for MAX chip to stabilize
  delay(5000);

  windowStartTime = millis();

  //initialize the variables we're linked to
  Setpoint = SETPOINT;

  //tell the PID to range between 0 and the full window size
  myPID.SetOutputLimits(0, WindowSize);

  //turn the PID on
  myPID.SetMode(AUTOMATIC);

}

void loop() {

  double temperature = thermocouple.readCelsius();
  if (!isnan(temperature)) {
    Input = temperature;
  }

  myPID.Compute();
  unsigned long now = millis();
  if (now - windowStartTime > WindowSize) {
    windowStartTime += WindowSize;
  }
  if (Output > now - windowStartTime) {

     digitalWrite(SSR, HIGH);
  } else {
      digitalWrite(SSR, LOW);
  }

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    Serial.print(EXPID);
    Serial.print(",");
    Serial.print(Input);
    Serial.print(",");
    Serial.print(Output);
    Serial.println();
  }
}









温度データ格納用テーブル作成



MySQL用のテーブル作成クエリです。



CREATE TABLE onsentamago

(

id INT(10) NOT NULL PRIMARY KEY AUTO_INCREMENT,

expid INT(10) ,

input VARCHAR(7),

output VARCHAR(7),

time TIMESTAMP DEFAULT CURRENT_TIMESTAMP()

);





温度データDB送信




Arduinoが1秒おきに吐き出す温度データを拾って、LAN上のMySQLサーバに送信するJavaのプログラムです。Mac上で実行し、Arduinoとの通信にはシリアル通信を使います。MySQLとの接続にはJDBCを使います。MacだとArduinoとの通信ポートは /dev/tty.usbmodem1421 のようになります。

Arduino and Java にあったサンプルプログラムにDB接続部を追加して作りました。イベントリスナでシリアル通信を待ち構える形になっています。


/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package dbtemp;

/**
 *
 * @author Tora
 */
import java.sql.Statement;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;

import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Enumeration;

public class Onsentamago implements SerialPortEventListener {

    SerialPort serialPort;
    /**
     * The port we're normally going to use.
     */
    private static final String PORT_NAMES[] = {
        "/dev/tty.usbmodem1411", "/dev/tty.usbmodem1421" // Mac OS X
      //"/dev/ttyACM0", // Raspberry Pi
    ,"/dev/tty/ACM1", // Linux      // "COM3", // Windows     };
    /**      * A BufferedReader which will be fed by a InputStreamReader converting the      * bytes into characters making the displayed results codepage independent      */     private BufferedReader input;
    /**      * The output stream to the port      */     private OutputStream output;
    /**      * Milliseconds to block while waiting for port open      */     private static final int TIME_OUT = 2000;
    /**      * Default bits per second for COM port.      */     private static final int DATA_RATE = 9600;

    public void initialize() {          // the next line is for Raspberry Pi         //          System.setProperty("gnu.io.rxtx.SerialPorts", "/dev/tty.usbmodem1411");
        CommPortIdentifier portId = null;         Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();
        //First, Find an instance of serial port as set in PORT_NAMES.         while (portEnum.hasMoreElements()) {             CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();             for (String portName : PORT_NAMES) {                 if (currPortId.getName().equals(portName)) {                     portId = currPortId;                     break;                 }             }         }
        if (portId == null) {             System.out.println("Could not find COM port.");             return;         }

        try {             // open serial port, and use class name for the appName.             serialPort = (SerialPort) portId.open(this.getClass().getName(),                     TIME_OUT);
            // set port parameters             serialPort.setSerialPortParams(DATA_RATE,                     SerialPort.DATABITS_8,                     SerialPort.STOPBITS_1,                     SerialPort.PARITY_NONE);             // open the streams             input = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));             output = serialPort.getOutputStream();
            // add event listeners             serialPort.addEventListener(this);             serialPort.notifyOnDataAvailable(true);
        } catch (Exception e) {             System.err.println(e.toString());         }     }

    /**      * This should be called when you stop using the port. This will prevent      * port locking on platforms like Linux.      */     public synchronized void close() {         if (serialPort != null) {             serialPort.removeEventListener();             serialPort.close();         }     }
    /**      * Handle an event on the serial port. Read the data and print it.      */     public synchronized void serialEvent(SerialPortEvent oEvent) {         if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {             try {                 String inputLine = input.readLine();                 String data[] = inputLine.split(",", 0);

System.out.println("ExpId=" + data[0]);                 System.out.println("Input=" + data[1]);                 System.out.println("Output=" + data[2]);
                Connection con = null;                 String url = "jdbc:mysql://192.168.1.2:3306/test";                 String user = "onsentamago";                 String password = "(๑´ڡ`๑) ";                 Timestamp timestamp = new Timestamp(System.currentTimeMillis());                 String dateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(timestamp);                 try {                     con = DriverManager.getConnection(url, user, password);                     Statement smt = con.createStatement();                     String sql = "INSERT into onsentamago (expid, input,output, time) values ('"+ expid +"','"+data[1]+"', '"+data[2]+"','" + dateTime + "') ";                    int i = smt.executeUpdate(sql);                     System.out.println(i);                 } catch (SQLException e) {                     System.out.println(e.toString());                 }
if(con!=null){                     con.close();                 }             } catch (Exception e) {                 System.err.println(e.toString());             } finally{             }         }
        // Ignore all the other eventTypes, but you should consider the other ones.     }
    public static void main(String[] args) throws Exception {         Onsentamago main = new Onsentamago();         main.initialize();         Thread t = new Thread() {             public void run() { //the following line will keep this app alive for 1000 seconds,                 //waiting for events to occur and responding to them (printing incoming messages to console).                 try {                     Thread.sleep(1000000);                 } catch (InterruptedException ie) {                 }             }         };         t.start();         System.out.println("Started");     } }



温度表示部

PHPでMySQLにデータを取りにいき、HTMLで表示してJavaScriptで再読み込みしているだけの何のギミックもないものです。この部分はのちのち膨らませればいいと思いました。

<!DOCTYPE html> <html> <head>     <meta charset="utf-8">     <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">     <title>Tora's Onsentamago Kettle</title>     <link rel="stylesheet" href="css/site.css"> <script type="text/javascript">

<-- var timer = "1000"; function ReloadAddr(){ window.location.reload(); } setTimeout(ReloadAddr, timer); //--> </script> </head> <body class="docs"> <h3>Arduino Onsentamago Real-Time Temperature Monitoring</h3>
<?php $link = mysql_connect('localhost', 'root', ''); if (!$link) {     die('DB connection failed'.mysql_error()); }
$db_selected = mysql_select_db('test', $link); if (!$db_selected){     die('No such database'.mysql_error()); }
mysql_set_charset('utf8'); $result = mysql_query('SELECT input, output, time FROM onsentamago WHERE  time > now() - INTERVAL 20 SECOND limit 5'); if (!$result) {     die('Query failed'.mysql_error()); } print('温泉の設定温度は67℃です(๑´ڡ`๑)<br><br> '); print('<table border=\"0\">'); print('<tr><td width="100" align="center">温泉の温度 

<img src="img/onsen.png" width="20" height="20"></td>
<td width="80" align="center">PID</td>
<td align="center">時刻</td>
</tr><tr>'); while ($row = mysql_fetch_assoc($result)) {      print('<td align="center">'.$row['input'].'</td>');      print('<td align="center">'.$row['output'].'</td>');      print('<td>'.$row['time'].'</td></tr>'); } print('</table>'); $close_flag = mysql_close($link); ?>


以上でソフトウェア製造はおわりです。

つづく

0 件のコメント:

Powered by Blogger.