ラズパイPicoの機能をフル活用! シーケンサ基板の使い方

Logikara-PLCアイキャッチ
この記事にはプロモーションを含みます。
PCBGOGOバナー600_1
PCBGOGOバナー600_2

Raspberry Pi Pico/PicoWの機能を最大限に活かし、シーケンサー(PLCProgrammable Logic Controller)として使用するための便利な動作確認基板について詳しく紹介します。

ここで紹介する基板は「ラズパイPico」をシーケンサーのように使用できるように作ったものです。
LEDや端子台、OLED表示器等を実装しているため、「ラズパイPico」の機能をフル活用するための動作確認用基板としても便利に使用することができます。
基板を製造するためのガーバーデータも公開していますので、このデータを使用して基板を発注することもできます。

シーケンサーは、産業用途や自動化システムで広く普及しており、複数の入出力を容易に制御するために使用されています。
産業用なので価格は高価ですが、小規模な用途では「ラズパイPico」の入出力点数(入力12点/出力6点)があれば十分な場合も多く、安価にシーケンサーと同じ動作を実現することができます。

小規模な用途で安価に、シーケンサのように使えるものがあれば便利と思い作ったものが今回の基板です。
「ラダー図」程ではないですが「Python」でラダーのようにプログラミングが書けるため、小規模な用途では手軽に使えて、動作の修正やメンテナンスも容易にできるので、便利に使えるのではと思います。

「Python」でラダーのようにプログラムを描く方法や、今回動作確認するシーケンサ基板の回路や仕様については、以下のリンクで詳しく紹介しています。

ラズパイPicoシーケンサ、ラダーをPythonで書く方法
シーケンサのラダー図からPythonプログラムに置き換えて動作確認する方法を、Raspberry Pi Picoでサンプルプログラムを使って詳しく紹介します。
ラズパイPicoでシーケンサを作ろう(基板設計編)簡単基板見積り
Raspbrry Pi Picoを使用したシーケンサ基板の回路設計から基板の見積もり発注方法まで詳しく紹介。入力12点,出力6点,アナログ入力2点,I2C,UART,OLED表示付です。

「ラズパイPico」の使い方については、以下のリンクで詳しく紹介しています。

ラズパイPico/PicoWの使い方を3つの開発環境Python、ArduinoIDE、PlatformIOで紹介
Raspberry Pi Pico/Pico Wの使い方を端子配列からPython(MicroPython)とC言語の開発環境、Lチカ方法まで紹介。PythonはTonny、C言語はArduinoIDEとPlatformIOの3種類で詳しく紹介します。
スポンサーリンク

1.シーケンサー基板の詳細

今回紹介するシーケンサー基板の仕様は以下になります。

・入力12点、LED付(フォトカプラ絶縁)
・出力6点、LED付(トランジスタアレーで電流増幅)
・アナログ入力2点
・I2C通信
・UART通信
・OLED(SSD1306)表示
この基板は小規模なシーケンサー(PLC)の置き換え用に設計したものなので「LogikaraーPLC」と呼んでいます。

基本的な機能は「Raspberrey Pi Pico/PicoW」そのものなので、以下から「ラズパイPico」の端子配列と合わせて「LogikaraーPLC」の端子配列や回路について紹介します。

出力は5Vリレーの駆動を想定しています。その他の用途での使用は、出力回路と使用するトランジスタアレーの仕様を確認してください。

Raspberry Pi Pico 端子配列

「Raspberry Pi Pico」の端子配列は下画像のようになります。(公式サイトより抜粋

ラズパイ(Raspberry Pi) Pico 端子配列
Wi-Fi通信機能付きの「Pico W」も「Pico」と端子配列は同じで、LEDの端子番号のみ異なります。
Wi-Fi通信機能を使用すれば、遠隔操作・監視のできるシーケンサーが安価で実現できます。

シーケンサー基板(Logikara-PLC)端子配列

「LogikaraーPLC」の端子配列は下画像のようになります。
「Raspberry Pi Pico」のほぼ全ての端子が端子台と繋がっているため、配線も簡単にできます。

ラズパイ シーケンサ端子配列

上画像の中の「オレンジ色」で表示したものが「LogikaraーPLC」の端子記号で、「ピンク色」で表示したものが「Raspberry Pi Pico」の端子機能です。

入力端子12点(X00〜X07、X10〜X13)と出力端子6点(Y00〜Y05)には動作確認用のLEDが実装されています。


画像内には実装されていませんが、OLED表示器も実装することができ、タイマーやカウンターの値、アナログ入力値等を表示させて確認することができます。

OLED表示器(SSD1306)も実装した実際の基板は下画像のようになります。

Logikara-PLC部品実装、半田付け

Raspberry Pi Pico 電源回路

「Raspberry Pi Pico」基板には以下のような電源回路が実装されています。(pico-datasheetより抜粋)

Raspberry Pi Pico 電源回路(VSYS端子)詳細

回路図のように「Raspberry Pi Pico」基板上には「VBUS」「VSYS」「3V3」の端子があり、以下のような機能があります。

  • VBUS:USBの5Vがそのまま出力されます。
  • VSYS:VBUSからショットキーバリアダイオード(D1)経由で約5V(5V-約0.3Vのため約4.7V)が出力されます。外部から1.8V〜5.5Vを供給して使用することもできます。
  • 3.3V:VSYS電源から電源IC(U2)によって生成された3.3Vが出力されており、コントローラICの電源として使用されています。

各電源端子は「LogikaraーPLC」基板では以下のように使用しています。

  • VBUS:「OUT 5V」と表示しています。外部デバイスやセンサ等に5Vを供給する時に使用します。
  • VSYS:「VSYS 5V」と表示しています。入出力端子のLED用電源や各端子台の5V電源出力用として使用しています。
  • 3.3V:「Raspberry Pi Pico」本体基板ではコントローラ制御用の3.3Vとして使用しているため、外部ノイズの影響を受けないように、極力「LogikaraーPLC」基板では使用せず、OLED表示器の電源用としてのみ使用し、基板外にも引き出さないようにしています。
VSYS端子の「VSYS 5V」には外部から5V電源を供給して使用することができます。
「Raspberry Pi Pico」基板単体としては「1.8〜5.5V」を供給しても動作しますが、「LogikaraーPLC」基板では「VSYS 5V」の端子全てが供給電圧になるため注意が必要です。
電圧が低いと入力端子ON時のLEDが暗かったり、点灯しないため、この場合は供給電圧に合わせて回路の抵抗値を調整する必要があります。

「LogikaraーPLC」基板の入出力回路は以下になります。

入力回路

ラズパイPicoシーケンサKicad回路図

外部配線からのノイズの影響を受けないようにフォトカプラを使用して電気的に絶縁しています。

出力回路

ラズパイPicoシーケンサKicad回路図

トランジスタアレーを使用して「Raspberry Pi Pico」基板単体では扱えない大きな電流を扱えるようにしています。(5Vリレーの駆動を想定しています。)

出力回路で

「LogikaraーPLC」の回路詳細については、以下のリンクで詳しく紹介しています。

ラズパイPicoでシーケンサを作ろう(基板設計編)簡単基板見積り
Raspbrry Pi Picoを使用したシーケンサ基板の回路設計から基板の見積もり発注方法まで詳しく紹介。入力12点,出力6点,アナログ入力2点,I2C,UART,OLED表示付です。
スポンサーリンク

2.サンプルプログラム(基本動作確認)

「LogikaraーPLC」基板を使用して、基本的な「Lチカ」「OLED表示」「アナログ入力」についてサンプルプログラムで動作確認を行なっていきます。

・Lチカ

基板上に実装されている「LED」を使用して下画像のように「Lチカ」の動作確認を行います。

Lチカ動作確認

電源を入れると「ラズパイPico」の「本体LED(緑)」が点灯します。
「X00ーCOM」間にスイッチを接続しておきます。

Lチカ動作確認

スイッチを押すと「X00」の「LED(緑)」が点灯し「Y00」の「LED(赤)」も点灯します。

スイッチはなんでもいいですが、今回は「ジャンパーリード(オスーメス)」に「タクトスイッチ(サンハヤト製)」を接続して下画像のように準備しています。

動作確認用スイッチ
動作確認用スイッチ

「Lチカ」のサンプルプログラムは以下になります。
※下の黒塗り部の右上アイコンクリックでコピーできます。

from machine import Pin  # 入出力モジュールを準備

# 入力ピン設定
sw = Pin(6, Pin.IN, Pin.PULL_UP)  # スイッチのピン番号を指定してswとして入力設定(プルアップ)

# 出力ピン設定
led0 = Pin("LED", Pin.OUT)  # 本体LEDピンをledとして出力に設定
led1 = Pin(18, Pin.OUT)     # 外部LEDのピン番号を指定してledとして出力設定

# メイン処理 ---------------------------------------------------------
while True:  # ずっと繰り返し
    led0.value(1)  #  本体LEDを点灯
    if sw.value() == 0:  # スイッチが押されていたら
        led1.value(1)    # LEDを点灯
    else:                # スイッチが押されてい なければ
        led1.value(0)    # LEDを消灯

・OLEDディスプレイ表示

OLEDディスプレイ(SSD1306)の動作は下画像のように「Hello!!」を表示するプログラムで確認を行いました。

ラズパイPicoシーケンサ動作確認OLED

OLEDディスプレイ(SSD1306)を使用するには「ライブラリ」のインストールが必要です。詳しい使用方法は以下のリンクで紹介しています。

ラズパイPicoでSSD1306有機ELディスプレイの使い方 MicroPython編
液晶表示器のないRaspberry Pi PicoでOLED SSD1306の使い方をMicroPythonのサンプルプログラムで紹介。開発環境はTonnyを使用します。

サンプルプログラムは以下になります。
※下の黒塗り部の右上アイコンクリックでコピーできます。

# Thonnyの「パッケージ管理」からssd1306で検索して「micropython-ssd1306」をインストールしておく

from machine import I2C  # I2C制御用モジュールを準備
import ssd1306  # 液晶表示器用ライブラリを使用
import time     # タイマーモジュールを準備

# I2C設定
i2c = I2C(1, sda=2, scl=3)  # (I2C識別ID 0or1, SDA, SCL)

# 使用するSSD1306のアドレスを取得して表示(通常は0x3C)
addr = i2c.scan()
print("OLED I2C Address :" + hex(addr[0]))

# ディスプレイ設定(幅, 高さ, 通信仕様)
display = ssd1306.SSD1306_I2C(128, 64, i2c)

# メイン処理 ---------------------------------------------------------
while True:  # ずっと繰り返し
    display.fill(0) # 画面表示初期化

    # 文字表示("表示内容", x座標, y座標, 色, フォント)
    display.text("OLED SSD1306", 17, 0, True)
    display.text("Hello!!", 37, 36, True)
    # 線の描画(始点x, 始点y, 終点x, 終点, 色)
    display.line(0, 9, 128, 9, True)
    # 四角の描画(x座標, y座標, 幅, 高さ, 色)
    display.rect(5, 20, 118, 40, True)

    display.show()  # 画面表示実行
    time.sleep(0.5) # 待ち時間

・アナログ入力

アナログ入力は下画像のように、ボリュームを使用して、2つのアナログ入力値(A1、A2)と電圧換算値をOLEDディスプレイに表示しています。

アナログ入力動作確認
アナログ入力動作確認

ボリュームは10kΩのものを使用し、抵抗4.7kΩをブレッドボードで接続して使用しています。
ボリュームはサンハヤト製のブレッドボード用部品(SBS-P01)のものを使用しています。

ボリューム回路図
ボリューム結線図
それぞれのボリュームを操作すると「0〜約3.3V」の電圧可変ができることが確認できます。

ボリューム(可変抵抗器)の使い方は以下のリンクで詳しく紹介しています。

ボリューム(可変抵抗器)の使い方、つなぎ方、抵抗値計算等を回路図も使って詳しく解説
ボリュームというと音量調節のイメージですが、明るさや回転数等を調整するのにも使用されます。「抵抗(固定抵抗器)」が固定の抵抗値を持つのに対して「可変抵抗器」は抵抗値を調整することができます。このボリューム(可変抵抗器)について紹介します。

ボリュームやタクトスイッチは以下のサンハヤト製ブレッドボード用部品(SBS-P01)が使いやすいです。

サンハヤトSBS -P01
サンハヤトSBS -P01

サンプルプログラムは以下になります。
※下の黒塗り部の右上アイコンクリックでコピーできます。

# Thonnyの「パッケージ管理」からssd1306で検索して「micropython-ssd1306」をインストールしておく

from machine import Pin, ADC, I2C  # 使用する制御用モジュールを準備
import ssd1306  # 液晶表示器用ライブラリを使用
import time     # タイマーモジュールを準備

# I2C設定
i2c = I2C(1, sda=2, scl=3)  # (I2C識別ID 0or1, SDA, SCL)

# 使用するSSD1306のアドレスを取得して表示(通常は0x3C)
addr = i2c.scan()
print("OLED I2C Address :" + hex(addr[0]))

# ディスプレイ設定(幅, 高さ, 通信仕様)
display = ssd1306.SSD1306_I2C(128, 64, i2c)

# アナログ入力ピン設定
adc1 = ADC(27)  # ADC1:アナログ入力(A0)の端子番号27を設定
adc2 = ADC(28)  # ADC2:アナログ入力(A1)の端子番号28を設定

# メイン処理 ---------------------------------------------------------
while True:  # ずっと繰り返し
    adc_val1 = 65535 - adc1.read_u16()    # アナログ入力(ADC)値1を取得
    adc_volt1 = (adc_val1 * 3.3) / 65535  # アナログ入力(ADC)値1を電圧(3.3V)に変換
    adc_val2 = 65535 - adc2.read_u16()    # アナログ入力(ADC)値2を取得
    adc_volt2 = (adc_val2 * 3.3) / 65535  # アナログ入力(ADC)値2を電圧(3.3V)に変換
    print("adc_val = {:5d}, {:5d}  adc_volt = {:5.2f}V, {:5.2f}V".format(adc_val1, adc_val2, adc_volt1, adc_volt2))  # 結果を表示する

    # 液晶画面表示内容設定
    display.fill(0)  # 表示内容消去
    display.text('Analog Input', 15, 0, True)  # タイトル表示
    display.hline(0, 10, 128, True)  # 指定座標から横線
    display.text('ADC1 ={:6d}'.format(adc_val1), 17, 15, True)    # アナログ入力1値表示
    display.text('VOLT1={:5.2f}'.format(adc_volt1), 17, 27, True) # 電圧1表示
    display.hline(10, 38, 108, True)  # 指定座標から横線
    display.text('ADC2 ={:6d}'.format(adc_val2), 17, 43, True)    # アナログ入力2値表示
    display.text('VOLT2={:5.2f}'.format(adc_volt2), 17, 55, True) # 電圧2表示

    # 設定した内容の表示実行
    display.show()

    time.sleep(1.0)  # 待ち時間

この他にも、「I2C通信」や「UART通信」についても、以下のリンクでサンプルプログラムを使用して詳しく紹介しています。

ラズパイPicoでシーケンサを作ろう(動作確認編)基板製造、部品実装
Raspbrry Pi Picoで製作したシーケンサ(PLC)基板の動作確認(入出力、ラダー、I2C/UART通信等)を行います。基板の発注方法も詳しく紹介していきます。
スポンサーリンク

3.シーケンサーとしての使い方

このまま「ラズパイPico」の動作確認用基板としても使用できますが、この基板は複数の入出力を制御することに特化して設計したため、小規模なシーケンサーとして使用することができます。

次から、シーケンサーとして使うための方法を詳しく紹介します。

シーケンサ制御を行うためには「ラダー図」を使用します。
「ラズパイPico」に「ラダー図」を直接書き込むことはできないため、今回は「Python」で「ラダー図」のような動作を書く方法を紹介します。

以下のような「ラダー図」を「Python」で書いてみます。

まずは、この動作を「if〜else 文」で書くと以下になります。

if (x0 == True and x2 == False) or m0 == True:
    m0 = True
if x1 == True:
    m0 = False

if m0 == True:
    y0 = True
else:
    y0 = False

これでもラダー図の動作を再現できていますが、毎回これを書くのは大変ですし、読みにくいです。

このため、以下のように省略して書くことで、簡単に書くことができ、簡潔で見やすくすることができます。

m0 = (x0 and not x2) or m0 if not x1 else False
y0 = m0

上記の書き方を、ラダー図と見比べると、1行づつがラダー図そのものを表現していることが確認できると思います。

慣れないうちは大変に感じるかもしれませんが、パターンは決まっているので、大規模な「ラダー図」でも、簡単に「Python」で置き換えることができます。

シーケンサで開発した制御プログラムを「ラズパイ Pico」のようなマイコンボードに簡単に置き換えることができるため、試作から量産基板への移行も容易になります。
さらに、プログラムに変更があった場合でも「ラダー図」の修正から置き換えるだけで、細かいタイミング調整を気にする必要がないため、デバッグも容易になります。

シーケンサーのラダー図の動作を「Python」で書く方法は、ラダー図の基本から以下のリンクで詳しく紹介しています。

ラズパイPicoシーケンサ、ラダーをPythonで書く方法
シーケンサのラダー図からPythonプログラムに置き換えて動作確認する方法を、Raspberry Pi Picoでサンプルプログラムを使って詳しく紹介します。

4.サンプルプログラム(シーケンサー)

実際にシーケンサーとしての動作を「Python」で書く方法を、サンプルプログラムを使用して確認していきましょう。

・ラダー図での動作確認(Pythonでラダー動作を描く方法)

基本的なラダー動作を「Python」で書いて、サンプルプログラムで確認していきます。
今回のプログラムでは以下の動作を確認します。

・a接点:「X0」がONで「Y0」がON
・b接点:「X0」がONで「Y1」がOFF

・AND:「X0」と「X1」がONで「Y2」がON
・OR:「X2」または「X3」がONで「Y3」がON
各動作の「ラダー図」はサンプルプログラム内に書いてあります。

サンプルプログラムは以下になります。
※下の黒塗り部の右上アイコンクリックでコピーできます。

from machine import *  # 制御用モジュールを準備

# 本体LED(GP25)を出力設定
led = Pin("LED", Pin.OUT)

# INPUT:入力端子(x0〜x3)設定(プルアップ)
input_pins = [6, 7, 8, 9]  # 使用する入力端子を設定(リスト作成)
inputs = [Pin(pin, Pin.IN, Pin.PULL_UP) for pin in input_pins]

# OUTPUT:出力端子(y0〜y3)設定
output_pins = [18, 19, 20, 21]  # 使用する出力端子を設定(リスト作成)
outputs = [Pin(pin, Pin.OUT) for pin in output_pins]

# 初期化
x0 = x1 = x2 = x3 = False  # 入力接点(4点)
y0 = y1 = y2 = y3 = False  # 出力リレー(4点)
m0 = m1 = m2 = m3 = m4 = m5 = m6 = m7 = m8 = m9 = \
m10 = m11 = m12 = m13 = m14 = m15 = m16 = m17 = m18 = m19 = False  # 補助リレー(20点)

# メイン処理 ---------------------------------------------------------------
while True:  # ずっと繰り返し
    # 入力状態スキャン
    x0, x1, x2, x3 = not inputs[0].value(), not inputs[1].value(), not inputs[2].value(), not inputs[3].value()

    # ************* シーケンス処理(ラダーから変換) *************
    '''
    # a接点、b接点 *********************
        x0
      --| |------------------( y0 )--
        x0
      --|/|------------------( y1 )--
    '''
    # Pythonへ変換(基本ラダー例)
    y0 = x0
    y1 = not x0

    '''
    # AND、OR *************************
        x0    x1
      --| |---| |------------( y2 )--
        x2
      --| |-┬----------------( y3 )--
        x3  |
      --| |-┘
    '''
    y2 = x0 and x1
    y3 = x2 or x3

    # 本体LED 点灯
    led.value(True)

    # ************* シーケンス処理 ここまで *************

    # 出力実行
    outputs[0].value(y0)
    outputs[1].value(y1)
    outputs[2].value(y2)
    outputs[3].value(y3)

・タイマ、カウンタの使い方

・タイマー割り込みによる常時タイマカウント

「ラズパイ Pico」のプログラムで遅延時間を簡単に作る時は「time.sleep(1.0)」のように書きますが、この場合、タイマカウント中は割り込み処理を設定しない限り他の処理を受け付けなくなります。

入力端子ごとに割り込み処理を設定しても良いですが、「タイマー割り込み」を使用して、シーケンサの「リフレッシュ方式」を実装することで、全ての入出力端子で並列処理しているような動作を実現することができます。

最小の処理速度は「1スキャンタイム(全ラダーを一回処理する時間)」に依存するため、ラダー処理が長くなると遅くなります。
大規模なラダーの場合は「1スキャンタイム」が「タイマー割り込み」の「割り込み時間間隔」を超えないように注意する必要があります。

サンプルプログラム内では以下のようにタイマー割り込みを実装しています。

#タイマON/OFF状態格納変数
t0 = t1 = t2 = t3 = t4 = t5 = t6 = t7 = t8 = t9 = False  # タイマ(10点)

# タイマ割り込み間隔設定(ms)
PERIOD = 100

# 汎用タイマ初期設定
timer_start = [False] * 10          # タイマカウント開始信号
TIM_SET = [0] * len(timer_start)    # タイマカウント値設定用初期値0(x100ms)
time_value = [0] * len(timer_start) # タイマカウント現在値格納用

# インターバルタイマ初期設定
TIM_INTERVAL0 = 5  # インターバルタイマON/OFF間隔(x0.1s)
tim_interval0 = 0  # インターバルタイマカウント現在値格納用
flicker = False    # フリッカON/OFF出力用

# タイマー割り込み発生時実行関数 ----------------------------------------------
def timer_callback(timer):
    # グローバル変数を宣言
    global TIM_INTERVAL0, tim_interval0, flicker
    global timer_start, time_value

    # 汎用タイマカウント処理
    t_values = []  # タイマのON/OFF結果を保持するリストを準備

    # 汎用タイマカウント(汎用タイマの数だけループ)
    for i in range(len(timer_start)):
        if timer_start[i] and time_value[i] < TIM_SET[i]:  # カウント条件ONでカウント値が設定値より小さければ
            time_value[i] += 1                             # タイマカウント+1
            #print("T{} = {}".format(i, time_value[i]))
        t_values.append(time_value[i] == TIM_SET[i])       # タイマカウントが完了したかを確認しON/OFF結果を格納
        time_value[i] = 0 if not timer_start[i] else time_value[i]  # カウント条件OFFなら初期化

    # 汎用タイマのON/OFF結果をタイマ変数に設定(汎用タイマの数だけループ)
    for i, value in enumerate(t_values):
        globals()[f"t{i}"] = value

    # インターバルタイマ(フリッカ)
    if not flicker:  # インターバルOFFならカウントアップ
        tim_interval0 += 1
        flicker = (tim_interval0 == TIM_INTERVAL0)  # カウントMAXでインターバルOFF
    else:            # インターバルONならカウントダウン
        tim_interval0 -= 1
        flicker = not (tim_interval0 == 0)  # カウントMAXでインターバルOFF

# タイマー割り込み設定(間隔, モード, コールバック関数)
timer = Timer()  # タイマーオブジェクトを作成
timer.init(period=PERIOD, mode=Timer.PERIODIC, callback=timer_callback)

2行目」で「タイマON/OFF状態格納変数」を準備しています。
タイマはラダー処理の条件式内で複数回記述することになるので、簡潔に書くためにリストではなく変数で個別に宣言しています。

5行目」でタイマ割り込み時間間隔を「ms」単位で設定します。
この時間がタイマカウントの最小値になります。
時間を短くする場合はラダー処理の「1スキャンタイム」を超えないように注意する必要があります。

8行目」で使用するタイマの数を指定しています。初期値は10点です。
タイマを増やす場合は「タイマON/OFF状態格納変数」を増やして、ここの要素数も増やします。

13〜15行目」では汎用的に使用できる「0.5秒のフリッカ(ON/OFF繰り返し)タイマ」を準備しています。必要に応じて使用してください。
サンプルプログラムでは「ラズパイ Pico」本体のLEDを点滅させるために使用しています。

18行目」からタイマー割り込み発生時に呼び出す関数を定義しています。
ラダー処理内でタイマのカウント条件が成立している時に、各タイマに設定された時間カウント処理が行われます。フリッカタイマのカウントもここで行います。

47,48行目」でタイマー割り込みを使用するための設定を行います。


タイマーを使用するにはメインループ内の「シーケンス処理」部に以下のように記入します。
以下は「T0:タイマ」を使用する例です。

'''
# T0 タイマ例 ****************************
    x2     t0
  --| |-┬-|/|-┬----------( m1 )--
    m1  |     | 
  --| |-┘     └--------(t0 K20)--
    m1
  --| |------------------( y1 )--
'''
# Pythonへ変換(T0 タイマ例)
m1 = x2 or m1 if not t0 else False
TIM_SET[0] = 20     # カウント時間設定(x0.1s)
timer_start[0] = m1 # タイマ0 のカウント条件として m1 を指定
y1 = m1

上コードの「12行目」でタイマのカウント時間を設定しています。
タイマ「T0」のカウント時間を設定するには「TIM_SET」リストの要素「0」に数値を指定します。

13行目」でタイマカウントを開始する条件を設定しています。
タイマ「T0」のカウント条件は「timer_start」リストの要素「0」に指定し、この条件が「True」になるとカウントが開始されます。
タイマカウントが終了するとタイマ変数「t0」が「True」になります。
カウント条件が「False」になると「t0」は「False」になり、カウント時間もリセットされます。


・カウンタークラス作成によるカウンタ

カウンタの回数カウントは「カウンタークラス」を作成して、必要な時に呼び出してカウントを行います。

# カウンタークラス
class Counter:
    # インスタンス変数
    def __init__(self):
        self.c_state = [False] * 10  # カウンタ状態格納用(10点)
        self.cnt = [0] * 10  # カウンタカウント数保持用(10点)
    
    # カウント実行(self, カウント条件, カウンタ番号, カウント値)
    def counter(self, coil, num, value):
        """
        カウンタの値を1つ増やします。
        Args:
            coil (bool): カウント条件が満たされたかどうかを表すブール値
            num (int): カウンタの番号
            value (int): カウントする最大値
        Returns:
            bool: カウンタが指定の最大値に達した場合はTrue、それ以外の場合はFalse
        """
        if coil and not self.c_state[num] and self.cnt[0] < value:
            self.cnt[num] += 1
            self.c_state[num] = True
            print("C = ", self.cnt[num])
            if self.cnt[num] == value:
                print("C{} ON!".format(num))
                return True
        if not coil:  # カウント条件がOFFなら カウンタ状態をFalseへ
            self.c_state[num] = False
        return False
    
    # カウント現在値取得
    def get_counter_value(self, num):
        return self.cnt[num]
    
    # カウント数リセット
    def reset_counter(self, num):
        self.cnt[num] = 0

# -------------------------------------------------------------------
count = Counter()  # 呼び出し元のプラグラムでカウンタクラスのインスタンスを作成

「カウンタークラス」内の「5,6行目」で使用するカウンタの数を指定しています。初期値は10点です。
カウンタを増やす場合は呼び出し元のカウンタ数に合わせて、要素数も増やします。

9行目」の「count.counter()」関数を呼び出すことでカウンタの回数カウントを実行します。
この関数は以下のように呼び出します。

カウンタ変数 = count.counter(カウント条件, カウンタ番号, カウント数
・カウンタ変数:c0 やc1 のように、カウンタ変数を指定します。カウント終了で「True」が格納されます。
カウント条件
:カウントする条件を設定します。x1 やm1 のように接点変数を指定してもいいですし、「x1 and not m1(x1がON かつ m1がOFF なら)」のように条件式を設定してもいいです。
カウンタ番号
:使用するカウンタ番号を、1 や2 のように数値で設定します。
カウント数
:カウント回数の設定値を、3 や10 のように数値で設定します。

実際は以下のように指定します。
以下の例ではカウンタ「c0」を使用して「x1」がONした回数を3回カウントします。

if not c0:  # カウンタC0がOFFなら
    c0 = count.counter(x1, 0, 3)  # カウント実行関数呼び出し(カウント条件, カウンタ番号, カウント数)
カウンタのカウント数が設定値に達したら「c0」が「True」になります。
「c0」のカウントが終了したらその後のカウントが実行されないように、上コード「1行目」 のように「if not c0:」の中でカウント関数を呼び出す必要があります。

31行目」でカウント現在値を取得する「count.get_counter_value()」関数を定義しています。
現在のカウント値を取得したい場合はカウンタ番号を指定して以下のように「count.get_counter_value()」呼び出します。

counter_value = count.get_counter_value(0)  # カウンタC0のカウント現在値を取得

35行目」でカウンタをリセットする「count.reset_counter()」関数を定義しています。
カウンタをリセットするときは、リセットしたいカウンタ番号を指定して以下のように「count.reset_counter()」呼び出します。

if x1:  # X1がONなら
    c0 = False  # カウンタC0をOFF
    count.reset_counter(0)  # カウンタC0のカウント数を0リセット

上コードでは「x1」がONしたらカウンタ「c0」を「False」に設定し、リセット関数を呼び出し、カウント数をリセットしています。


・本格的なシーケンサとしての使用例

「タイマ」や「カウンタ」を含んだ本格的なラダー動作をサンプルプログラムで確認します。

コピペで動作確認できるように「タイマ割り込み」や「カウンタクラス」も1つのファイル内に書いているので非常に長くなってますが、ラダー動作部はサンプルプログラムの「153〜206行目」になります。
この部分を、必要なラダー動作に置き換えて動作確認してみてください。

サンプルプログラムでは以下の3つの基本動作のラダーが書いてあります。
(ラダー図はコード内にコメントで書いています。)

①自己保持回路:「X0」がONすると「M0」が自己保持され「Y0」がONします。
「X1」がONすると「M0」の自己保持が解除され「Y0」がOFFします。
②タイマ動作:「X2」がONすると「M1」が自己保持され「T0」タイマカウントが開始し「Y2」がONします。タイマのカウントが終了すると「M1」の自己保持が解除され「Y2」がOFFします。
③カウンタ動作:「X3」を押すごとに「C0」カウンタが+1され「Y2」が点灯します。
カウント値が設定値に達すると「C0」がONし「Y3」がONします。
「X1」がONすると「C0」カウントがリセットされます。

OLED表示器には下画像のように「アナログ入力値」と「タイマ」「カウンタ」の設定値(括弧書き)と現在値を表示しています。

Logikara-PLCラダー動作確認サンプルプログラム
アナログ入力処理(A/D変換)やOLED表示処理時間がラダー処理に影響を与えないように「ラズパイPico」の「マルチスレッド(並列)処理」を使用しています。
「Core0」でラダー処理を実行し、「Core1」でOLED表示等の別の処理を実行するようにしています。

「ラズパイPico」の「マルチスレッド(並列)処理」については以下のリンクで詳しく紹介しています。

ラズパイPicoマルチコアで並列動作させる方法:MicroPython編
Raspberry Pi Picoのデュアルコアを使用して2つの処理を同時に並列処理させる使い方について詳しく紹介。一定間隔で実行させたいプログラムが簡単に実現できます。

サンプルプログラムは以下になります。
※下の黒塗り部の右上アイコンクリックでコピーできます。

import _thread  # スレッド(並列動作)用モジュールを準備
from machine import Pin, I2C, ADC, Timer  # 制御用モジュールを準備
import ssd1306  # 液晶表示器用ライブラリ

# I2C設定
i2c1 = I2C(1, sda=2, scl=3)  # SSD1306用 (I2C識別ID 1, SDA, SCL)

# OLED 表示設定
i2c1_addr = i2c1.scan()[0]  # 使用するSSD1306のアドレスを取得して表示(通常は0x3C)
print("OLED I2C1 Address :" + hex(i2c1_addr))
display = ssd1306.SSD1306_I2C(128, 64, i2c1)  # ディスプレイ設定(幅, 高さ, 通信仕様)

# アナログ入力ピン設定
adc1 = ADC(27)  # アナログ入力(A0)の端子番号27を設定
# adc2 = ADC(28)  # アナログ入力(A1)の端子番号28を設定

# 本体LED(GP25)を出力設定
led = Pin(25, Pin.OUT)

# INPUT:入力端子(x0〜x3)設定(プルアップ)
input_pins = [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]  # 使用する入力端子を設定(リスト作成)
inputs = [Pin(pin, Pin.IN, Pin.PULL_UP) for pin in input_pins]

# OUTPUT:出力端子(y0〜y3)設定
output_pins = [18, 19, 20, 21, 22, 26]  # 使用する出力端子を設定(リスト作成)
outputs = [Pin(pin, Pin.OUT) for pin in output_pins]

# グルーバル変数初期化
x0 = x1 = x2 = x3 = x4 = x5 = x6 = x7 = x10 = x11 = x12 = x13 = False  # 入力接点(12点)
y0 = y1 = y2 = y3 = y4 = y5 = False  # 出力点数(6点)
m0 = m1 = m2 = m3 = m4 = m5 = m6 = m7 = m8 = m9 = \
m10 = m11 = m12 = m13 = m14 = m15 = m16 = m17 = m18 = m19 = False  # 補助リレー(20点)
t0 = t1 = t2 = t3 = t4 = t5 = t6 = t7 = t8 = t9 = False  # タイマ(10点)
c0 = c1 = c2 = c3 = c4 = c5 = c6 = c7 = c8 = c9 = False  # カウンタ(10点)

# タイマ割り込み間隔設定(ms)
PERIOD = 100

# 汎用タイマ初期設定
timer_start = [False] * 10          # タイマカウント開始信号
TIM_SET = [0] * len(timer_start)    # タイマカウント値設定用初期値0(x100ms)
time_value = [0] * len(timer_start) # タイマカウント現在値格納用

# 汎用カウンタ初期設定
count_value = [0] * 10 # タイマカウント現在値格納用
CNT_SET = [0] * len(count_value)

# インターバルタイマ初期設定
TIM_INTERVAL0 = 5  # インターバルタイマON/OFF間隔(x0.1s)
tim_interval0 = 0  # インターバルタイマカウント現在値格納用
flicker = False    # フリッカON/OFF出力用

# タイマー割り込み発生時実行関数 ----------------------------------------------
def timer_callback(timer):
    # グローバル変数を宣言
    global TIM_INTERVAL0, tim_interval0, flicker
    global timer_start, time_value

    # 汎用タイマカウント処理
    t_values = []  # タイマのON/OFF結果を保持するリストを準備

    # 汎用タイマカウント(汎用タイマの数だけループ)
    for i in range(len(timer_start)):
        if timer_start[i] and time_value[i] < TIM_SET[i]:  # カウント条件ONでカウント値が設定値より小さければ
            time_value[i] += 1                             # タイマカウント+1
            #print("T{} = {}".format(i, time_value[i]))
        t_values.append(time_value[i] == TIM_SET[i])       # タイマカウントが完了したかを確認しON/OFF結果を格納
        time_value[i] = 0 if not timer_start[i] else time_value[i]  # カウント条件OFFなら初期化

    # 汎用タイマのON/OFF結果をタイマ変数に設定(汎用タイマの数だけループ)
    for i, value in enumerate(t_values):
        globals()[f"t{i}"] = value

    # インターバルタイマ(フリッカ)
    if not flicker:  # インターバルOFFならカウントアップ
        tim_interval0 += 1
        flicker = (tim_interval0 == TIM_INTERVAL0)  # カウントMAXでインターバルOFF
    else:            # インターバルONならカウントダウン
        tim_interval0 -= 1
        flicker = not (tim_interval0 == 0)  # カウントMAXでインターバルOFF

# タイマー割り込み設定(間隔, モード, コールバック関数)
timer = Timer()  # タイマーオブジェクトを作成
timer.init(period=PERIOD, mode=Timer.PERIODIC, callback=timer_callback)

# カウンタ処理クラス ---------------------------------------------------------
class Counter:
    # インスタンス変数
    def __init__(self):
        self.c_state = [False] * 10  # カウンタ状態格納用(10点)
        self.cnt = [0] * 10  # カウンタカウント数保持用(10点)
    
    # カウント実行(self, カウント条件, カウンタ番号, カウント値)
    def counter(self, coil, num, value):
        """
        カウンタの値を1つ増やします。
        Args:
            coil (bool): カウント条件が満たされたかどうかを表すブール値
            num (int): カウンタの番号
            value (int): カウントする最大値
        Returns:
            bool: カウンタが指定の最大値に達した場合はTrue、それ以外の場合はFalse
        """
        if coil and not self.c_state[num] and self.cnt[0] < value:
            self.cnt[num] += 1
            self.c_state[num] = True
            print("C = ", self.cnt[num])
            if self.cnt[num] == value:
                print("C{} ON!".format(num))
                return True
        if not coil:  # カウント条件がOFFなら カウンタ状態をFalseへ
            self.c_state[num] = False
        return False
    
    # カウント現在値取得
    def get_counter_value(self, num):
        return self.cnt[num]
    
    # カウント数リセット
    def reset_counter(self, num):
        self.cnt[num] = 0

counter = Counter()  # カウンタのインスタンスを作成

# カウンタ処理関数 ----------------------------------------------------------
def count(coil, num, value):  #カウント処理 (カウント条件, カウンタ番号, カウント設定値)
    global count_value  # カウント現在値(液晶表示用)
    global CNT_SET      # カウント設定値(液晶表示用)

    CNT_SET[num] = value  # カウンタ設定値の取得(液晶表示用)
    result = counter.counter(coil, num, value)         # カウント結果の取得
    count_value[num] = counter.get_counter_value(num)  # カウント現在値の取得

    return result

def count_reset(num):  # カウントリセット処理
    global count_value  # カウント現在値(液晶表示用)
    
    count_value[num] = 0        # カウンタ0リセット(液晶表示用)
    counter.reset_counter(num)  # カウンタリセット(カウンタ番号)


# ラダー処理(Core0) ---------------------------------------------------------------
def ladder():
    global m0,m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11,m12,m13,m14,m15,m16,m17,m18,m19
    global c0,c1,c2,c3,c4,c5,c6,c7,c8,c9
    while True:  # ずっと繰り返し
        # 入力状態スキャン
        x0, x1, x2, x3 = not inputs[0].value(), not inputs[1].value(), not inputs[2].value(), not inputs[3].value()
        x4, x5, x6, x7 = not inputs[4].value(), not inputs[5].value(), not inputs[6].value(), not inputs[7].value()
        x10, x11, x12, x13 = not inputs[8].value(), not inputs[9].value(), not inputs[10].value(), not inputs[11].value()

        # ************* シーケンス処理(ラダーから変換) *************
        '''
        # 自己保持動作確認 *************************
            x0    x1
          --| |-┬-|/|------------( m0 )--
            m0  |
          --| |-┘
            m0
          --| |------------------( y0 )--
        '''
        # Pythonへ変換(基本ラダー例)
        m0 = x0 or m0 if not x1 else False
        y0 = m0

        '''
        # T0 タイマ例 ****************************
            x2     t0
          --| |-┬-|/|-┬----------( m1 )--
            m1  |     | 
          --| |-┘     └--------(t0 K20)--
            m1
          --| |------------------( y1 )--
        '''
        # Pythonへ変換(T0 タイマ例)
        m1 = x2 or m1 if not t0 else False
        TIM_SET[0] = 20     # カウント時間設定(x0.1s)
        timer_start[0] = m1 # タイマ0 のカウント条件として m1 を指定
        y1 = m1

        '''
        # C0 カウンタ例 ****************************
            x3
          --| |---------------( c0 K3 )--
            x1
          --| |--------------( RST c0 )--
            x3    c0
          --| |---|/|------------( y2 )--
            c0  
          --| |------------------( y3 )--
        '''
        # Pythonへ変換(C0 カウンタ例)
        if not c0:  # カウント開始条件
            c0 = count(x3, 0, 3)  # (カウント条件, カウンタ番号, カウント設定値)
        if x1:  # カウンタリセット条件
            c0 = False
            count_reset(0)  # カウンタリセット(カウンタ番号)
        y2 = x3 and not c0
        y3 = c0

        # 本体LED動作(常時点滅、m0がONなら点灯)
        led_state = flicker or m0
        led.value(led_state)

        # ************* シーケンス処理 ここまで *************

        # 出力実行
        outputs[0].value(y0)
        outputs[1].value(y1)
        outputs[2].value(y2)
        outputs[3].value(y3)
    
# 補助機能処理 A/D変換、OLED表示(Core1) ----------------------------------------------------
def sub_function(t_set, t, c_set, c):
    CNT_DISP = 10  # ADC1更新間隔
    cnt_disp = CNT_DISP  # ADC1更新間隔初期化
    adc_volt1 = 0
    while True:  # ずっと繰り返し
        # 液晶画面表示内容設定
        display.fill(0)  # 表示内容消去

        # ADC表示
        cnt_disp -= 1  # カウント -1
        if cnt_disp == 0:  # ADC1値更新
            cnt_disp = CNT_DISP  # 更新間隔初期化
            adc_val1 = adc1.read_u16()    # アナログ入力(ADC)値1を取得
            adc_volt1 = (adc_val1 * 3.3) / 65535  # アナログ入力(ADC)値1を電圧(3.3V)に変換
        display.text('ADC1:{:5.2f}V'.format(adc_volt1), 5, 0, True) # ADC1電圧表示
        display.hline(0, 10, 128, True)  # 指定座標から横線

        # タイマ表示    
        display.text('T0:{:3d} ({:d})'.format(t[0], t_set[0]), 5, 15, True)
        display.text('T1:{:3d} ({:d})'.format(t[1], t_set[1]), 5, 27, True)
        display.hline(0, 38, 128, True)

        # カウンタ表示
        display.text('C0:{:3d} ({:d})'.format(c[0], c_set[0]), 5, 43, True)
        display.text('C1:{:3d} ({:d})'.format(c[1], c_set[1]), 5, 55, True)
        # 設定した内容の表示実行
        display.show()

_thread.start_new_thread(sub_function, (TIM_SET, time_value, CNT_SET, count_value))  # Core1処理
ladder()  # Core0処理
コピペするには長くなりすぎました(汗)せめてカウンタクラスは別ファイルとして読み込もうと思いましたが、コピペで動作できるように1つのファイル内で書いています。
本当はカウンタの値は、電源を切っても保持できるよう「Pico」の本体内に保存したかったですが、さらに長くなるため今回は省略しています。
カウンタ等の現在値を保持したい場合は、以下のリンクを参考に追加してみてください。
ラズパイPico本体にデータ保存、起動時に読込む方法:Python編
Raspberry Pi Picoで変数データを本体フォルダに保存して起動時に読込む方法。電源OFFしても値を保持するカウンタのサンプルプログラムで詳しく紹介

5.ガーバーデータの公開

この基板を製造・発注するためのガーバーデータを以下からダウンロードできます。

以下のリンク先で基板の仕様や回路図をサンプルプログラムと一緒に公開しています。

ラズパイPicoでシーケンサを作ろう(基板設計編)簡単基板見積り
Raspbrry Pi Picoを使用したシーケンサ基板の回路設計から基板の見積もり発注方法まで詳しく紹介。入力12点,出力6点,アナログ入力2点,I2C,UART,OLED表示付です。
ラズパイPicoでシーケンサを作ろう(動作確認編)基板製造、部品実装
Raspbrry Pi Picoで製作したシーケンサ(PLC)基板の動作確認(入出力、ラダー、I2C/UART通信等)を行います。基板の発注方法も詳しく紹介していきます。
公開しているデータは、メインの基板のみで、面付けしている動作確認用のサブ基板は含みません。

実際に基板製造を依頼する場合は、以下の注意点をご確認の上、自己責任にてご使用をお願いします。

・ダウンロードしたガーバーデータの内容は念の為「ガーバービューワーソフト(以下で紹介)」で内容をよくご確認いただき、問題ないことをご確認の上ご使用をお願いします。
・この基板は趣味のレベルで制作したものですので、個人の趣味や学習用途でのみご使用ください。
・仕様に関しては上記のリンク先で十分に検証、動作確認を行なっておりますが、個人の趣味レベルの確認のため、発生した、いかなる不具合、損害に関して一切保証はできません。
・公開しているデータについて、個人利用以外での加工、再配布は禁止いたします。
PCBGOGOバナー600_1
PCBGOGOバナー600_2

ガーバーデータの確認方法

基板製造を行うための「ガーバーデータ」の確認は「ガーバービューワー」ソフトで行います。

フリーの基板設計ソフト「KiCAD」等でも確認できますが、以下の「PCBGOGO」さんのサイトではダウンロード不要で、ウェブサイト上で「ガーバーデータ」の内容が確認できるページがあるので、確認方法を紹介します。

ガーバーデータの確認には以下のリンクをクリックします。

ガーバービューア | Gerber Viewer - PCBGOGO
PCBGOGOのガーバービューア(Gerber Viewer)でガーバーデータを確認しましょう。ガーバーデータをアップロードすれば、ガーバーデータを閲覧できます。

以下のようなページが表示されたら画面中央の「+」アイコンをクリックします。

PCBGOGOガーバーデータの確認

パソコンのフォルダが表示されたら、読み込むガーバーデータを選択します。

読み込むデータは、各レイヤーのガーバーデータを含んだ「圧縮ファイル」である必要があります。
PCBGOGOガーバーデータの確認

データが読み込まれると以下のように、基板の表「top」画像が表示されます。

PCBGOGOガーバーデータの確認

「bottom」をクリックすると基板裏面のデータが確認できます。

PCBGOGOガーバーデータの確認

「layers」をクリックすると、各レイヤーのデータが個別に確認できます。
初期画面は見にくいので、以下画面左のレイヤー項目右のアイコン部で「Shift+Click」すると、圧縮ファイルの中の各レイヤーのデータが個別に確認できます。

PCBGOGOガーバーデータの確認
基板の製造依頼をするときは、事前にガーバービューワーでデータに問題ないか確認してから行いましょう。

6.ガーバーデータから基板を発注する方法

・基板製造・発注なら「PCBGOGO」

基板製造業者はたくさんありますが「安価」で「短納期」の「PCBGOGO」さんがおすすめです。

「PCBGOGO」さんは、中国のメーカーですが、発注ページも日本語でわかりやすく、10年以上の実績があり、世界中にプリント基板製造と部品実装のサービスを提供しています。

ウェブサイトでは、24時間のオンラインカスタマーサービスもあり、発注ごとに日本語のできるスタッフが担当してくれるため、何かあったときはメールで問い合わせすることもできます。

基板の製造枚数は5枚から発注可能で、日本だと数万円する基板でも「PCBGOGO」さんなら数千円と、個人でも手軽に発注できる価格です。新規会員登録なら数百円で発注できます。

ガーバーデータから基板を発注する方法は、以下のリンクでより詳しく紹介していますので、こちらを参照してください。(この基板と同じガーバーデータで紹介しています。)

個人でも格安、簡単、自作プリント基板製造、発注方法
基板製造は数千円で5日もあれば注文できる今、個人の電子工作でもフリーソフトのKiCAD等で製作したガーバーデータで簡単にプリント基板を製造依頼する方法を詳しく紹介します。

・見積もり価格の確認

基板製造の見積もり価格は、以下ボタンから「PCBGOGO」さんのホームページで「基板寸法」と「枚数」を入力するだけで簡単に確認できるので、価格だけでも確認してみてください。安いです♪

PCBGOGOジャパン

以下のようなページが表示されたら[自動お見積もり]ボタンをクリックします。

格安基板製造PCBGOGO

以下のページが表示されたら[外形寸法(長さ×幅)]と[枚数]を入力して、[見積作成]ボタンをクリックします。
(今回はページ上の「5.ガーバーデータの公開」でデータを使用して、基板寸法「90 x 80.7mm」枚数「5枚」で確認しています。)

格安基板製造PCBGOGO
基板の「材質」や「レジスト色」「面付け」「層数」等、細かな設定もできますが、一般的な材質「FR-4」で「両面」基板なら他に何も設定する必要はありません。

以下のようにページ右に見積もり金額が表示されます。

格安基板製造PCBGOGO


新規会員登録の場合は初回のみ割引が受けられるので、必ず以下のように2箇所の[新規ユーザー割引]に「チェック」を入れましょう。

格安基板製造PCBGOGO
「基板製造価格」と「送料」の合計金額が表示されますが、その下に割引適用後の価格が表示されます。新規会員登録の場合、今回の基板は「$1、送料無料」で製造できます。

$1」で基板製造を依頼するには「新規会員登録」が必要なため[今すぐ登録!]をクリックして、会員登録を行いましょう。

会員登録から基板発注までの手順は以下のリンクでより詳しく紹介しています。

個人でも格安、簡単、自作プリント基板製造、発注方法
基板製造は数千円で5日もあれば注文できる今、個人の電子工作でもフリーソフトのKiCAD等で製作したガーバーデータで簡単にプリント基板を製造依頼する方法を詳しく紹介します。
会員登録が完了したら、再度見積もりページから情報を入力後[カートに入れる]をクリックして「ガーバーデータ」の登録を行なっていきます。
データ確認後に価格が確定するため、価格に問題なければ、支払いを行って発注完了です。
「面付け」や「レジスト色」の指定を行った場合は価格が変わる可能性があります。
最終的な価格はデータ確認後に確定します。
また、支払い方法によって別途手数料(5%)がかかります。
PCBGOGOバナー600_1
PCBGOGOバナー600_2

7.部品リスト

「LogikaraーPLC」に使用した部品リストは以下になります。
全て「秋月電子通商」さんで購入できるので、秋月電子さんの品番も載せています。

記号品名型式メーカ秋月電子品番数量単価合計備考
C1, C3, C4セラミックコンデンサRDER71H104K0P1H03B村田製作所P-135823¥10¥300.1uF10個入 100円
C2電解コンデンサ16MH5100MEFC6.3X5ルビコンP-050021¥20¥20100uF
D1〜14発光ダイオードOSG8HA3Z74AOptoSupplyI-1163712¥10¥180
D13〜18発光ダイオードOSR5JA3Z74AI-115776¥10¥60
R1〜18炭素皮膜抵抗1/6W 390ΩR-1639118¥1¥18390Ω100本 100円
R19炭素皮膜抵抗1/6W 10kΩR-161031¥1¥110kΩ100本 100円
U1〜12フォトカプラTLP785 GBランク東芝I-0755412¥20¥240

U13Raspberry Pi PicoRaspberry Pi PicoRaspberry PiM-161321¥770¥770

SSD1306OLEDディスプレイSSD1306SUNHOKEYP-120311¥580¥580

U14トランジスタアレーULN2003ANHTC KoreaI-150641¥40¥40

RESET1タクトスイッチDTS-63-F-N-V-RED(TS-0606-F-N-RCoslandP-036461¥10¥10黒、白、青、黄もあり
J1〜9,11〜13ターミナルブロック 2PAPF-102PHOENIX CONTACTP-083694¥20¥80

ターミナルブロック 3PAPF-103PHOENIX CONTACTP-08370 10¥30¥300

J6ピンソケット 4PFH-1x4SG/RHUseconn ElectronicsC-100991¥20¥20
(Pico)ピンソケット 20PFH-1×20C-030772¥50¥100

(Pico)ピンヘッダ
40P
PH-1x40SGC-001672¥35¥704Pと
20Px2に
分割して使用
ゴム足P-100801¥60¥604個入
合計¥2519

8.部品の半田付け手順

基板に実装する部品は全てDIP部品を使用しているので、比較的簡単に半田付けできると思いますが、背の低い部品から順番に半田付けしていくと、より簡単に半田付けできるので、おすすめの手順を以下で紹介します。

実装する部品は以下のようになります。

Logikara-PLC部品一式
写真ではフォトカプラが2個足りなかったり、2Pの端子台が1個多かったりしてます(汗)部品数量は部品リストでご確認ください。

まずは一番背の低い抵抗から、リードを曲げて基板に差し込んで半田付けしていきます。

Logikara-PLC部品実装、半田付け

「R19」のみ「10kΩ」なので先に差し込み、残りは全て「390Ω」を差し込みます。

Logikara-PLC部品実装、半田付け

ひっくり返して押し付けると、抵抗が浮かずに半田付けできるのでこの状態でまとめて半田付けしましょう。

半田付け時には部品が高温になり樹脂やゴム板等を敷いていると溶けるため、焦げてもいい木の板等を敷きましょう。
Logikara-PLC部品実装、半田付け

抵抗を全て半田付けしたら、リードをカットします。

Logikara-PLC部品実装、半田付け

次に背の低い「フォトカプラ」と「トランジスタアレー」を半田付けします。

Logikara-PLC部品実装、半田付け

「フォトカプラ」と「トランジスタアレー」には向きがあります。
「フォトカプラ」の「丸印」や「トランジスタアレー」の「切り欠き」を、基板のシルク印刷の切り欠き部と合わせて差し込んで半田付けします。

Logikara-PLC部品実装、半田付け

「発光ダイオード(LED)」にも向きがあります。
リード(足)の長い方を丸ランド(基板の外側)に差し込みましょう。

Logikara-PLC部品実装、半田付け

全てリードの長い方を外側に差し込んで、基板裏に出たリードは曲げておくと安定します。

Logikara-PLC部品実装、半田付け

ひっくり返して半田付けしますが、リードの長い方を先に半田付けして、向きを整えてからもう片方のリードを半田付けすると綺麗に半田付けできます。

Logikara-PLC部品実装、半田付け

次に「コンデンサ」を半田付けしていきます。
「電解コンデンサ(C2)」には向きがあるので、上画像の向きで差し込みます。
「セラミックコンデンサ」には向きはありません。

Logikara-PLC部品実装、半田付け

「コンデンサ」もリードを曲げておき、ひっくり返して半田付けします。

Logikara-PLC部品実装、半田付け

次に、端子台を上画像のように準備します。
ここで「タクトスイッチ」を基板に差し込んでおきましょう。

Logikara-PLC部品実装、半田付け

「端子台」は組み合わせることができるので、上画像のように組み合わせておきます。

Logikara-PLC部品実装、半田付け

「端子台」を組み合わせたら、基板に差し込んで、ひっくり返して、「タクトスイッチ」と一緒に半田付けします。

Logikara-PLC部品実装、半田付け

次に「ラズパイPico」に「ピンヘッダー」を半田付けします。「40Pのピンヘッダー」を20Pづつに割って準備しておきます。

Logikara-PLC部品実装、半田付け

「ピンヘッダー」は1個づつ半田付けすると位置合わせが大変なので、上画像のようにブレッドボードに挿しておくと安定します。

Logikara-PLC部品実装、半田付け

「ピンヘッダー」に「ラズパイPico」を載せて半田付けします。

Logikara-PLC部品実装、半田付け

「ピンヘッダー」が半田付けできたら、「20Pのピンソケット」を準備します。

Logikara-PLC部品実装、半田付け

「ピンソケット」は上画像のように「ピンヘッダー」に差し込んでおきます。

Logikara-PLC部品実装、半田付け

「ラズパイPico」は実装向きがあるので注意しましょう。

Logikara-PLC部品実装、半田付け

上画像のような向きで基板にのせて、ひっくり返して半田付けします。

Logikara-PLC部品実装、半田付け

最後に「OLED表示器」と「4Pのピンソケット」を半田付けします。

Logikara-PLC部品実装、半田付け

「ピンソケット」は「OLED表示器」に取り付けておきましょう。

Logikara-PLC部品実装、半田付け

「OLED表示器」は安定しないので、1箇所半田付けして位置を整えながら、残りの箇所を半田付けしていきます。

Logikara-PLC部品実装、半田付け

以上で全ての部品の半田付けが完了しました。

9.まとめ

Raspberry Pi Picoの機能を最大限に活かし、シーケンサ(PLC)として簡単にラダープログラムをPythonで作成、実行できる基板について詳しく紹介しました。

この基板は、LEDや端子台、OLED表示器等を実装しているため、「ラズパイPico」の動作確認用基板としても便利に使用することができます。

シーケンサーは、産業用途や自動化システムで広く普及しており、複数の入出力を容易に制御するために使用されていますが、産業用なので価格は高価です。

小規模な用途で安価に、シーケンサのように使えるものがあれば便利と思い作ったものが今回の基板で、「ラダー図」程ではないですが「Python」でラダーのようにプログラミングが書けるため、小規模な用途では手軽に使えて、動作の修正やメンテナンスも容易にできるので、便利に使えるのではと思います。

基板製造のための「ガーバーデータ」も公開しました。今は個人でも基板製造を安価で発注できるようになってますので、興味のある方は基板を作って動作確認してみてください。

個人でも格安、簡単、自作プリント基板製造、発注方法
基板製造は数千円で5日もあれば注文できる今、個人の電子工作でもフリーソフトのKiCAD等で製作したガーバーデータで簡単にプリント基板を製造依頼する方法を詳しく紹介します。
ラズパイPicoでシーケンサを作ろう(基板設計編)簡単基板見積り
Raspbrry Pi Picoを使用したシーケンサ基板の回路設計から基板の見積もり発注方法まで詳しく紹介。入力12点,出力6点,アナログ入力2点,I2C,UART,OLED表示付です。
PCBGOGOバナー600_1
PCBGOGOバナー600_2

コメント

タイトルとURLをコピーしました