保温器の製作 (PIC編)

以前に納豆を作りたくてESP32を使って保温器を製作しました。CPUにESP32を使っているのですが、この保温器はWiFi機能を使用せずESP32以外のCPUに変更したいなと思っていました。そこで、ここまで説明して来た事を元にこの保温器のCPUをPICに変えよと思います。

作り変えるなら

実は今の保温器にちょっと不自由に思っている点が有ります。

  • ハード関係
    • 温度設定用ロータリースイッチの表示が小さくて見づらい。
    • ESP32がむき出しになっていて操作する時に触ってしまうかもしれない
    • グラフィックLCD(AQM1248)の表示が若干小さい。
  • ソフト関係
    • 設定の温度になるまでに時間がかかる(約30分)
    • 設定の温度より実際の温度が低めとなる。

新しく作るなら、これらについて改善したと思います。

先ずはハード

以下は、CPUをPIC(PIC24FJ64GA002)に置き換えた回路と実際の配線です。

  • 変更したモジュールとその理由を以下にまとめました
項目変更前変更後理由
 CPU ESP32 PIC24FJ64GA002  WiFI無しCPU 
 表示部 AQM1218(グラフィック)  AQM1602(キャラクター)  表示が大きい
 温湿度測定素子  AM2320 DHT20 値段が安い
  • PIC24FJ64GA002の電源は3.3Vなので、5Vから3.3VにNJU7223(レギュレータ)を使って変換しています。
  • ユーザーインターフェイス部分(回路の矢印部分)を本体から切り離して棚の上に、本体は棚の下に配置しました。

次はソフト

使っているデバイスで共通のものも有りますが、全てのソフトを書き換えています。

項目機能デバイス名関連ソフト詳細
温度管理 保温器の温度を管理 PIC24FJ64G main.c 下記参照
表示器 データの表示 AQM1602 AQM1602.c
AQM1602.h
AQM1602 Character LCD (for PIC)
温度測定 ヒーター温度測定 ADT7410 ADT7410.c
ADT7410.h
ADT7410 温度測定モジュール (for PIC)
温湿度測定 チャンバー内温湿度測定 DHT20 DHT20.c
DHT20.h
DHT20 温湿度モジュール (for PIC)
通信 I2C通信 PIC24FJ64GA i2c_1.c
i2c_1.h
PIC24fJ I2C1用ソフト

main.c:温度を管理しているプログラム。前回から下記を変更しています。

  • 起動したらロータリースイッチの値をキャラクターLCDに表示
    • ロータースイッチの表示が小さくて見難いの対策です。
    • 0.5sec間隔でスイッチをスキャンして値を表示しています。
    • これにより変更されたスイッチの値がLCDに表示されるので非常に見やすくなりました。
  • スタートボタンを押したら5秒間隔で測定データをLCDに表示
    • 前回同様温度のサンプリング間隔を5秒としています。
    • このサンプリングに合わせて測定データを表示しています。
  • ストップボタンを押したら、温度設定モードに戻る。
    • 電源投入時と同じモードです。
    • 0.5sec間隔でスイッチをスキャンして値を表示しています。
  • 設定温度になるまでに時間がかかる問題と設定温度とのズレが出る問題
    • 前回はヒーターの温度のみで保温器の中の温度を制御して来ました。
    • 今回は保温器の中の温度も制御用データとして使用し、また時間で制御方法を変えています。
      1. スタートしてから10分間 
        • 前回:ヒーターが規定の温度に達したらオフ
        • 今回:ヒーターが規定の温度に達しても器内の温度が設定値の67%より低ければオフしない。
      2. スタートしてから10分以降
        • 前回:ヒーターが規定の温度に達したらオフ
        • 今回:ヒーターが規定の温度に達しても器内の温度が設定値の95%より低ければオフしない。
main.c

/*
  main.c 
*/

#include "mcc_generated_files/system.h"
#include "mcc_generated_files/tmr1.h"
#include "mcc_generated_files/ext_int.h"
#include "i2c_1.h"
#include <xc.h>
#include "AQM1602.h"
#include "DHT20.h"
#include "ADT7410.h"
#include "string.h"
#define FCY      _XTAL_FREQ
#include <libpic30.h> 

#define     LED_Pilot       LATAbits.LATA0
#define     LED_Heat        LATAbits.LATA1
#define     Heat_Power      LATAbits.LATA2
#define     St_Btn          PORTBbits.RB2

#define     w5_sec          0x9896
#define     w500ms          0xF41

#define     ADT7410_addr    0x49

#define     NUM_L0          PORTBbits.RB6
#define     NUM_L2          PORTBbits.RB7
#define     NUM_L4          PORTBbits.RB10
#define     NUM_L8          PORTBbits.RB11

#define     NUM_H0          PORTBbits.RB12
#define     NUM_H2          PORTBbits.RB13
#define     NUM_H4          PORTBbits.RB14
#define     NUM_H8          PORTBbits.RB15

int get_target_temp(void);

int int_flg = 0;

void myTimerISR(void)
{
    int_flg = 2;
}
 
int main(void)
{
    char c_buf[30];
    int target_temp, state_flg, tm_cnt, past_time;
    float ht_temp, heat_tmp, h_data[2];

    int a;
    
    // initialize the device
    SYSTEM_Initialize();

    InitI2C_Master();
    init_LCD();
    
    TMR1_SetInterruptHandler (myTimerISR);

    state_flg = 0;

    while (1)
    {
        TMR1 = 0x00;
        TMR1_Start();
        int_flg = 0;
        while(!int_flg) ;
        
        switch(int_flg)
        {
            case 1: // Start or Stop
                    a = 0; 
                    if(!state_flg)
                    {
                        a = 1;
                        target_temp = get_target_temp();
                        heat_tmp = target_temp * 1.92 - 24.14;
                        tm_cnt = past_time = 0;
                    }

                    LED_Pilot = LED_Heat = Heat_Power = a;

                    state_flg = !state_flg;
                    
                    for(a = 0; a< 5; a++)
                    {
                        while(!St_Btn) ;
                        __delay_ms(10);
                    }
                    
                    PR1 = w500ms;
                    break;
                    
            case 2: // Controll Heater
                    LCD_Cls();
                    if(state_flg)
                    {
                        ADT7410_get_temp(ADT7410_addr, &ht_temp);
                        DHT20_Get_Data(h_data);
                        a = 1;
                        if(ht_temp > heat_tmp)
                        {
                            if(past_time < 10)
                            {
                                if( h_data[1] > (target_temp * 0.67)) a = 0; 
                            }
                            else
                            {
                                if( h_data[1] > (target_temp * 0.95)) a = 0; 
                            }                            
                        }
                        LED_Heat = Heat_Power = a;
            
                        tm_cnt ++;
                        if(tm_cnt == 12) {tm_cnt = 0; past_time ++;}
                        
                        DHT20_Get_Data(h_data);

                        sprintf(c_buf,"T:%0.1f'c", h_data[1]);
                        LCD_Print_L(0,0,c_buf);
    
                        sprintf(c_buf,"H:%0.1f", h_data[0]);
                        LCD_Print_L(0,1,c_buf);
                        LCD_Print("% ");
                        sprintf(c_buf,"N:%d", past_time);
                        LCD_Print(c_buf);
                        PR1 = w5_sec;
                    }
                    else
                    {
                        a = get_target_temp();
                        sprintf(c_buf,"Target:%d'c",a);
                        LCD_Print_L(0,0,c_buf);
                        PR1 = w500ms;
                    }
                    break;
        }
    }

    return 1;
}

int get_target_temp()
{
  int a;

    a = 0; 
    if(NUM_H0) a = 1;
    if(NUM_H2) a += 2;
    if(NUM_H4) a += 4;
    if(NUM_H8) a += 8;
    a *= 10;

    if(NUM_L0) a += 1;
    if(NUM_L2) a += 2;
    if(NUM_L4) a += 4;
    if(NUM_L8) a += 8;
    
    return a;
}

/**
 End of File
*/

MCCを使った周辺機器の設定

今回は、EXT_INT、TMR1、GPIOポートの設定がメインです。

  • EXT_INT:スタート・ストップボタンを外部割り込みで処理しています。
    • スタート・ストップボタンはRB2に割り当てています。
    • トリガーはポジティブ。回路上はボタンを押して離した時となります。
    • 外部割り込みに関しては、MPLAB® Code Configurator(EXT_INT編)を参照
  • TMR1:LCDへの表示時間とサンプリング間隔を計算し割り込みをかける
    • 温度設定時の0.5秒、測定時の5秒間隔はTMR1を使って時間を計算しています。
    • 規定時間になったら割り込みを発生させ、状況をLCDに表示しています。
    • TMR1の詳細は、MPLAB® Code Configurator(Timer編)を参照
  • GPIO:温度設定用のロータリースイッチ、2つのLED、ヒーターのオンオフ、
部品機能ポート 入出力 WPU
ロータリースイッチ(10の位) 設定温度10の位の指定 RB12,13,14,15 入力 無し
ロータリースイッチ(1の位) 設定温度1の位の指定 RB6,7,10,11 入力 無し
パイロットランプ 保温動作中点灯 RA0 出力
ヒーターランプ ヒーター駆動時に点灯 RA1 出力
ヒーターポート ヒーター駆動用SSRのオンオフ RA2 出力
Start/Stopスイッチ 保温のスタート/ストップ
(外部割り込み)
RB2 入力 有り
  • その他
    • システムクロック:タイマーの都合上Foscを4MHzに設定しています。
    • タイマー:クロックを256分周し0.5秒のタイマーとしています。MCCではこれで設定し、
           ソフトでカウンターの値を切り替えて、0.5秒と5秒を切り替えています。
    • I2Cに関して:MCCでI2Cのセットアップを行ったら、機器からの受信が上手く動きませんでした。
            必要な部分のみ自分で書いています。MCCではセットアップしていません。

実行と結果

下記はプログラムの実行結果です。

  • 今回の保温器で設定温度を40℃として実行した結果:右
  • 前回の保温器で設定温度を50℃として実行した結果:左
  • 設定温度に達する時間
    • 前回と今回で設定温度が違いますが、設定温度に達する時間は今回の方が速い。
    • 保温器に冷却機能が無い為、前回はオーバーシュートしない設定としていました。
    • 今回は設定温度に達する時間は速いが、オーバーシュートしています。
  • 定常状態で設定温度より若干低い
    • 設定温度を挟んで上下に振れる。
    • 波形的には前回の方が綺麗。

全体的には制御方法を変更しない方が良かったかもしれません。

最後に

今回使用してプログラムを添付します。