シェルスクリプトマガジン

test

香川大学SLPからお届け!(Vol.78掲載)

投稿日:2022.05.25 | カテゴリー: コード

著者:樋口 史弥

アプリケーションやサービスは、ユーザーの意見や技術状況に合わせた変更をしやすいように開発するのが理想的です。そうした開発を実現するための設計手法の一つが「クリーンアーキテクチャ」です。今回は、クリーンアーキテクチャを用いて簡単な匿名掲示板を作る方法を解説します。

シェルスクリプトマガジン Vol.78は以下のリンク先でご購入できます。

図4 投稿削除機能を追加した場合のコード例

// メッセージを表す構造体
type struct Message {
(略)
}
// 全メッセージを取得する関数
func (m *Message)GetAllMessages() error {
(略)
}
// メッセージを投稿する関数
func (m *Message)Post() error {
(略)
}
// メッセージを削除する関数
func (m *Message)Delete() error {
(略)
}

図6 単一責任原則に沿ったコードの例

// メッセージを表す構造体
type struct Message {
(略)
}
// 一般ユーザーを表す構造体
type struct User {
    Message
(略)
}
// メッセージを投稿する関数
func (u *User)Post() error {
(略)
}
// 全メッセージを取得する関数
func (u *User)GetAllMessages() error {
(略)
}
// 管理者を表す構造体
func type struct Administrator {
    Message
(略)
}
// メッセージを削除する関数
func (a *Administrator)Delete() error {
(略)
}

図8 モジュールに依存したコードの例

func (m *Message)Post() error {
(略)
    // MySQLHandlerモジュールのExecute関数を直接実行
    err := MySQLHandler.Execute(sql)
(略)
}

図10 モジュールに依存したコードの例

type struct Message {
 (略)
    i DBInterface
}
func (m *Message)Post() error {
(略)
    // MySQLHandlerモジュールのExecute関数を
    // 直接実行せず、DBInterface経由で実行
    err := m.i.Execute(sql)
(略)
}

図19 messageモジュールのコード

type Message interface {
    // メッセージの文字列を返却する関数
    Message() string
}
type message struct {
    // メッセージの文字列を格納するメンバー
    detail string
}

図20 投稿時刻やハンドルを付加する場合のmessageモジュールのコード

type Message interface {
    // メッセージの文字列を返却する関数
    Message() string
    // 投稿したユーザーのハンドルを返却する関数
    HandleName() string
}
type message struct {
    // メッセージの文字列を格納するメンバー
    detail string
    // 投稿ユーザーのハンドルを格納るメンバー
    handleName string
}

図21 ログイン機能を付加するためのuserモジュールのコード

type User interface {
    // ユーザーIDを返却する関数
    ID() string
    // パスワードが適切かどうかを検査する関数
    MatchPassword(string) bool
}
type user struct {
    id string
    password string
}

図22 メッセージを投稿するというユースケースに対応するコード

type controller struct {
    // データベースに保存するモジュールに
    // アクセスするためのインタフェース
    database db_gateway.DB
}
func (c *controller)Post(m message.Message) error {
    // Messageの中身の検証
    if len(m.Message()) == 0 {
        return MessageNotFound
    }
    // インタフェースを用いてメッセージを保存
    err := c.database.RecordMessage(m)
(略)
}

図23 メッセージを保存するRecordMessageメソッドのコード

type mySQLHandler struct {
    // Frameworks&Drivers層のデータベースクライアントライブラリ
    // のモジュールを呼び出すインタフェース
    database mysql_gateway.MySQLHandler
}
// メッセージを保存するためのハンドラメソッド
func (m *mySQLHandler)RecordMessage(
    message message.Message,
) error {
    // SQL文を生成
    sql := 
    INSERT INTO messages
    (message)
    VALUES (?)
    
    // インタフェースを介してSQL文を実行
    _, err := m.database.Execute(sql, message.Message())
(略)
}

図24 mySQLHandler 構造体のdatabaseメンバーに所属するインタフェースの定義コード

type MySQLHandler interface {
    Query(string, ...interface{}) (Row, error)
    Execute(statement string, args ...interface{}) (Result, error)
}

図25 MySQLHandler インタフェースで定義されるExecute メソッドのコード

import (
(略)
    // データベースクライアントライブラリをインポート
    "database/sql"
)
type mysql struct {
    // データベースに問い合わせるためのコネクション
    Conn *sql.DB
}
func (handler *mysql) Execute(
    statement string,
    args ...interface{},
) (worker.Result, error) {
    res := new(SQLResult)
    // コネクションを用いてSQL文を実行
    result, err := handler.Conn.Exec(statement, args...)
(略)
    res.Result = result
    return res, nil
}

図26 匿名掲示板のMainコンポーネントのコード

func main() {
    // External Interfaceのdatabaseモジュールを初期化
    mysqlDB, f, err := database.NewMySQL(db_user, db_password, 
                                         db_ip, db_port, db_name)
(略)
    // Interface Adaptersのdb_handlerモジュールを初期化
    DBHandler := db_handler.NewMySQL(mysqlDB)
    // Application Business Rulesのcontrollerモジュールを初期化
    controller := interactor.NewController(DBHandler)
    // External Interfaceのserverモジュールを初期化
    server := server.New(port, controller)
    // Application Business Rulesのinteractorモジュールを初期化
    interactor := interactor.NewInteractor(server)
    // Application Business Rulesに制御を渡す
    e := interactor.Run()
(略)
}

図27 データの保存先を変更する際のMainコンポーネント書き換え箇所

func main() {
    // External Interfaceのdatabaseモジュールを初期化
    textDB, err := database.NewText(TextPath)
(略)
    // Interface Adaptersのdb_handlerモジュールを初期化
    DBHandler := db_handler.New(textDB)
    // Application Business Rulesのcontrollerモジュールを初期化
    controller := interactor.NewController(DBHandler)
    // External Interfaceのserverモジュールを初期化
    server := server.New(port, controller)
    // Application Business Rulesのinteractorモジュールを初期化
    interactor := interactor.NewInteractor(server)
    // Application Business Rulesに制御を渡す
    e := interactor.Run()
(略)
}

中小企業手作りIT化奮戦記(Vol.78掲載)

投稿日:2022.05.25 | カテゴリー: コード

著者:菅 雄一

筆者は2001年、PHP言語とOSS(オープンソースソフトウエア)データベース管理システムのPostgreSQLに出会った。それ以降、データベースが絡んだWebシステムを構築する際は、PHPとPostgreSQLを組み合わせて使っていた。しかし2021年に社内Web業務システムなどをレンタルサーバーに移行させた際には、PHPとOSSデータベース管理システムのMySQLを初めて組み合わせて使った。今回は、その移行作業について紹介する。

シェルスクリプトマガジン Vol.78は以下のリンク先でご購入できます。

図1 自作のメール送信用関数の定義コード

function Send_Mail($addr,$content)
{
  $fp = fsockopen(" メールサーバーのアドレス ",25, &$errno , &$errstr);
  if(!$fp)
  {
    print "$errstr ($errno)<br>n" ;
  }
  else
  {
    $subject = "Subject: 発送依頼書" ;
    $subject = mb_convert_encoding($subject,"UTF-8");
    $content = mb_convert_encoding($content,"JIS");
    fwrite($fp,"HELO server.example.co.jp\n");
    fwrite($fp,"MAIL FROM:chohyo@example.co.jp\n");
    fwrite($fp,"RCPT TO:$addr\n");
    fwrite($fp,"DATA\n");
    fwrite($fp,"From: chohyo@example.co.jp\n");
    fwrite($fp,"$subject\n");
    fwrite($fp,"Content-Type: text/plain; charset=ISO-2022-JP\n");
    fwrite($fp,"$content\n");
    fwrite($fp,".\n");
    fwrite($fp,"QUIT\n");
  }
  fclose($fp);
}

Pythonあれこれ(Vol.78掲載)

投稿日:2022.05.25 | カテゴリー: コード

著者:飯尾 淳

本連載では「Pythonを昔から使っているものの、それほど使いこなしてはいない」という筆者が、いろいろな日常業務をPythonで処理することで、立派な「蛇使い」に育つことを目指します。その過程を温
かく見守ってください。皆さんと共に勉強していきましょう。第8回は、Webアプリケーションフレームワーク「Flask」を使って基本的なWebアプリを作成する方法を紹介します。データベースにアクセスする方法やテンプレートの利用方法も解説します。

シェルスクリプトマガジン Vol.78は以下のリンク先でご購入できます。

図1 「Hello World」に相当するシンプルなWebアプリのコード(app.py)

from flask import Flask

app = Flask(__name__)
@app.route('/')
def hello():
  return '<h1>Hello World!</h1>'

図4 ルーティングの記述を追加したWebアプリのコード

from flask import Flask
app = Flask(__name__)

@app.route('/hello')
def hello():
  return '<h1>Hello World!</h1>'

@app.route('/goodbye')
def goodbye():
  return '<h1>Good-bye World!</h1>'

図5 パスパラメータを使うWebアプリのコードの例

from flask import Flask
app = Flask(__name__)

@app.route('/hello/<name>')
def hello(name):
  return f'<h1>Hello {name}!</h1>'

図7 クエリーパラメータを使うWebアプリのコードの例

from flask import Flask, request
app = Flask(__name__)

@app.route('/hello')
def hello():
  name = request.args['name']
  return f'<h1>Hello {name}!</h1>'

図9 メモ帳Webアプリのひな型コード

from flask import Flask, request
app = Flask(__name__)

@app.route('/addMemo')
def addMemo():
  name = request.args['name']
  description = request.args['description']
  return f'ADD: name = {name}, desc = {description}'

@app.route('/getMemo')
def getMemo():
  return f'[TBD]'

図10 データベースを使うメモ帳Webアプリのコード(その1)

from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Integer, String, Text

app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///memo.sqlite'

db = SQLAlchemy(app)

class Memo(db.Model):
  __tablename__ = 'memo'
  ID          = db.Column(Integer, primary_key=True)
  NAME        = db.Column(String(500))
  DESCRIPTION = db.Column(Text)

db.create_all()

@app.route('/addMemo')
def addMemo():
  name = request.args['name']
  description = request.args['description']
  return f'ADD: name = {name}, desc = {description}'

@app.route('/getMemo')
def getMemo():
  return f'[TBD]'

図12 データベースを使うメモ帳Webアプリのコード(その2)

from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Integer, String, Text

app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///memo.sqlite'

db = SQLAlchemy(app)

class Memo(db.Model):
  __tablename__ = 'memo'
  ID          = db.Column(Integer, primary_key=True)
  NAME        = db.Column(String(500))
  DESCRIPTION = db.Column(Text)

db.create_all()

@app.route('/addMemo')
def addMemo():
  name = request.args['name']
  description = request.args['description']
  db.session.add(Memo(NAME=name, DESCRIPTION=description))
  db.session.commit()
  db.session.close()
  return f'ADD: name = {name}, desc = {description}'

@app.route('/getMemo')
def getMemo():
  memos = db.session.query(
    Memo.ID, Memo.NAME, Memo.DESCRIPTION).all()
  return str(memos)

図14 テンプレートファイル「index.html」の内容

<!DOCTYPE html>
<html>
  <head>
    <title>Flask Test</title>
  </head>
  <body>
    <h1>The Memo List</h1>
    <table>
      <tr><th>ID</th><th>Name</th><th>Description</th></tr>
      {% for memo in memos: %}
      <tr><td>{{ memo.0 }}</td>
          <td>{{ memo.1 }}</td>
          <td>{{ memo.2 }}</td></tr>
      {% endfor %}
    </table>
  </body>
</html>

図15 テンプレートを使うメモ帳Webアプリのコード(その1)

from flask import Flask, request, render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Integer, String, Text

app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///memo.sqlite'

db = SQLAlchemy(app)

class Memo(db.Model):
  __tablename__ = 'memo'
  ID          = db.Column(Integer, primary_key=True)
  NAME        = db.Column(String(500))
  DESCRIPTION = db.Column(Text)

db.create_all()

@app.route('/addMemo')
def addMemo():
  name = request.args['name']
  description = request.args['description']
  db.session.add(Memo(NAME=name, DESCRIPTION=description))
  db.session.commit()
  db.session.close()
  return f'ADD: name = {name}, desc = {description}'

@app.route('/getMemo')
def getMemo():
  memos = db.session.query(
    Memo.ID, Memo.NAME, Memo.DESCRIPTION).all()
  return render_template('index.html', memos=memos)

図17 修正したテンプレートファイル「index.html」の内容

<!DOCTYPE html>
<html>
  <head>
    <title>Flask Test</title>
  </head>
  <body>
    <h1>The Memo List</h1>
    <table border="1" width="500" cellspacing="0"
           cellpadding="5" bordercolor="#333333">
      <tr><th>ID</th><th>Name</th><th>Description</th></tr>
      {% for memo in memos: %}
      <tr><td>{{ memo.0 }}</td>
          <td>{{ memo.1 }}</td>
          <td>{{ memo.2 }}</td></tr>
      {% endfor %}
    </table>
    <h2>New Memo</h2>
    <form action="/addMemo" method="post">
      <table style="text-align: left;">
        <tr style="vertical-align: top;"><th>Name:</th>
          <td><input type="text" name="name"></input></td></tr>
        <tr style="vertical-align: top;"><th>Description:</th>
          <td><textarea name="description" rows="4" cols="40">
              </textarea></td></td>
      </table>
      <input type="submit" value="Submit">
      <input type="reset" value="Reset">
    </form>
  </body>
</html>

図18 テンプレートを使うメモ帳Webアプリのコード(その2)

from flask import Flask, request, render_template, redirect, url_for
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import Integer, String, Text

app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///memo.sqlite'

db = SQLAlchemy(app)

class Memo(db.Model):
  __tablename__ = 'memo'
  ID          = db.Column(Integer, primary_key=True)
  NAME        = db.Column(String(500))
  DESCRIPTION = db.Column(Text)

db.create_all()

@app.route('/addMemo', methods=['POST'])
def addMemo():
  name = request.form['name']
  description = request.form['description']
  db.session.add(Memo(NAME=name, DESCRIPTION=description))
  db.session.commit()
  db.session.close()
  return redirect(url_for('getMemo'))

@app.route('/getMemo')
def getMemo():
  memos = db.session.query(
    Memo.ID, Memo.NAME, Memo.DESCRIPTION).all()
  return render_template('index.html', memos=memos)

Raspberry Piを100%活用しよう(Vol.78掲載)

投稿日:2022.05.25 | カテゴリー: コード

著者:米田 聡

小型コンピュータボード「Raspberry Pi」(ラズパイ)向けにさまざまな拡張ボードが発売されています。その拡張ボードとラズパイを組み合わせれば、ラズパイでいろいろなことが簡単に試せます。第11回は、光の反射で物体との距離を調べるセンサー搭載の拡張基板を扱います。

シェルスクリプトマガジン Vol.78は以下のリンク先でご購入できます。

図4 VCNL4010から値を読み出すPythonプログラム(sample.py)

import time
import Adafruit_VCNL40xx

vcnl = Adafruit_VCNL40xx.VCNL4010()

try:
  while True:
    prox = vcnl.read_proximity()
    ambient = vcnl.read_ambient()
    print('Proximity = ' + str(prox))
    print('Ambient = ' + str(ambient) + ' Lux')
    time.sleep(0.5)
except KeyboardInterrupt:
    pass

特集1 ESP32とラズパイで作る本格IoT(Vol.78記載)

投稿日:2022.05.25 | カテゴリー: コード

筆者:魔法少女

マイコン「ESP32」と小型コンピュータボード「Raspberry Pi」を使って本格的なIoT(Internet of Things、モノのインターネット)環境を構築してみましょう。本企画では、3軸の加速度センサーを用いて簡易地震計を作成し、リアルタイムに揺れをグラフで表示します。

シェルスクリプトマガジン Vol.78は以下のリンク先でご購入できます。

図4 Eclipse Mosquittoの設定スクリプト(mosquitto_setting.sh)

#!/bin/sh

sudo tee /etc/mosquitto/conf.d/confer.conf << EOF >dev/null
listener 1883
allow_anonymous true
listener 8081
protocol websockets
sudo systemctl reload mosquitto

図18 ESP32搭載ボートの起動時に無線LANに接続するプログラム(boot.py)

import network

SSID = 'SSID'
PASSWORD = 'パスワード'

wlan_if = network.WLAN(network.STA_IF)
wlan_if.active(True)
wlan_if.connect(SSID, PASSWORD)

図19 パブリッシャのプログラム(main.py)

import time
from machine import Pin, SoftI2C
import mpu6050
from umqtt.simple import MQTTClient
import json

mqtt_topic = "home/seismometer1"
publisher_id = "place_esp32"
broker_address = "192.168.1.100"
interval_time = "1"

i2c = SoftI2C(scl=Pin(22), sda=Pin(21))
accelerometer = mpu6050.accel(i2c)

time.sleep(5)

while True:
  iot_value = accelerometer.get_values()
  iot_value_json = json.dumps(iot_value)
  print(iot_value_json)
  publisher = MQTTClient(publisher_id,broker_address)
  publisher.connect()
  publisher.publish(mqtt_topic, msg=str(iot_value_json))
  publisher.disconnect()
  time.sleep(int(interval_time))

図24 ブスクライバとなるJavaScriptプログラムを含んだHTMLファイル(seismometer.html)

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>簡易地震計</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-streaming@1.9.0"></script>
  <script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
</head>
<body>
  <canvas id="myChart"></canvas>
  <script>
    const client = mqtt.connect('ws://raspibroker.local:8081');
    const ctx = document.getElementById('myChart').getContext('2d');
    var chart = new Chart(ctx, {
      type: 'line',
      data: {
        datasets: [{
          data: [],
          label: 'X軸',
          borderColor: 'rgb(255, 0, 255)',
          backgroundColor: 'rgba(255, 255, 255, 0)',
          lineTension: 0,
        }, {
          data: [],
          label: 'Y軸',
          borderColor: 'rgb(0, 255, 0)',
          backgroundColor: 'rgba(255, 255, 255, 0)',
          lineTension: 0,
        }, {
          data: [],
          label: 'Z軸',
          borderColor: 'rgb(0, 0, 255)',
          backgroundColor: 'rgba(255, 255, 255, 0)',
          lineTension: 0,
        }]
      },
      options: {
        scales: {
          xAxes: [{
            type: 'realtime',
            realtime: {
              delay: 2000,
            },
          }],
          yAxes: [{
            ticks: {
              min: -32767,
              max: 32767
            }
          }]
        }
      }
    });
    client.on('connect', () => {
      console.log('connected');
      client.subscribe('home/seismometer1');
    });
    client.on('message', (topic, message) => {
      console.log(message.toString());
      acc_json = JSON.parse(message.toString());
      chart.data.datasets[0].data.push({
        x: Date.now(),
        y: acc_json.AcX
      });
      chart.data.datasets[1].data.push({
        x: Date.now(),
        y: acc_json.AcY
      });
      chart.data.datasets[2].data.push({
        x: Date.now(),
        y: acc_json.AcZ - 16384
      });
      chart.update();
    });
  </script>
</body>
</html>

特集1 アプリケーション開発基盤 .NET(Vol.78記載)

投稿日:2022.05.25 | カテゴリー: コード

著者:松井 恵一、木下 舜、上原 将司

デスクトップパソコンやモバイル、Web、クラウドなど、さまざまな環境で動作するアプリケーションを開発できる基盤が「.NET」です。昨年11月に初の長期サポート版となる.NET 6がリリースされました。本特集では、最新の.NET 6を含め、.NETについて分かりやすく解説します。

シェルスクリプトマガジン Vol.78は以下のリンク先でご購入できます。

図13 Program.csファイルの中身

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

図14 C#で記述した、「Hello World」を表示するプログラムの例

using System;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine("Hello World!");
        }
    }
}

図15 MyFirstDotnetApp.csprojファイルの中身

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

シェルスクリプトマガジンvol.78 Web掲載記事まとめ

投稿日:2022.05.25 | カテゴリー: コード

004 レポート OpenSSH 9.0/9.0p1リリース
005 レポート ゲームエンジンのUnreal Engine 5
006 製品レビュー マグカップ型電気鍋「Cook Mug」
007 NEWS FLASH
008 特集1 アプリケーション開発基盤 .NET/松井恵一、木下舜、上原将司 コード掲載
023 Hello Nogyo!
024 特集2 UMLを使い始めてみよう/森三貴
040 特別企画 ESP32とラズパイで作る本格IoT 簡易地震計/魔法少女 コード掲載
058 Raspberry Piを100%活用しよう/米田聡 コード掲載
060 Pythonあれこれ/飯尾淳 コード掲載
066 法林浩之のFIGHTING TALKS/法林浩之
068 中小企業手作りIT化奮戦記/菅雄一 コード掲載
072 ダークウェブ/桑原滝弥、イケヤシロウ
074 香川大学SLPからお届け!/樋口史弥 コード掲載
084 タイ語から分かる現地生活/つじみき
092 Bash入門/大津真
098 Techパズル/gori.sh
099 コラム「計画は全体を、問題は点を追求する」/シェル魔人

Vol.78

投稿日:2022.05.25 | カテゴリー: バックナンバー

 世の中では、Windows/Linuxパソコン、Mac、スマートフォン、タブレット、Raspberry Piなどのさまざまなコンピュータが利用されています。通常、アプリケーションを開発する場合、CPUやOS、表示するディスプレイなどの動作環境の違いを意識したプログラミングをしなくてはいけません。
 そのような実行環境をあまり意識することなく、一つのソースコードからさまざまなコンピュータで動作するアプリケーションを開発できるのが、マイクロソフトが提供するアプリケーション開発基盤「.NET」です。以前は、実行環境に合わせて「.NET Framework」「.NET Core」「Xamarin」に分かれていましたが、.NET 5で.NETとして統合。昨年11月には、.NET初の長期サポート版となる「.NET 6」が提供されました。
 特集1では、この.NET 6を中心として、.NETの歴史や.NETを構成する主要な要素、.NET Frameworkと.NETの比較を解説し、サンプルを交えた.NETアプリケーション開発を解説します。
 システム開発の全工程をカバーする表現力を持ち、ISO(国際標準化機構)やJIS(日本産業規格)で規格化されている表記法に「UML」(統一モデリング言語)があります。UMLでは、さまざまな図を使ってシステムの仕様や設計情報などを表現します。特集2では、UMLの概要や活用する効果、よく使われる図について分かりやすく紹介します。
 特別企画では、マイコン「ESP32」と小型コンピュータボード「Raspberry Pi」を使って、簡易地震計を製作しました。Webブラウザ上に揺れをリアルタイムにグラフ表示します。
 このほか、今回から「製品レビュー」を開始しました。エンジニア向けに、便利で面白いグッズやサービスを多数紹介していきます。
 今回も読み応え十分のシェルスクリプトマガジン Vol.78。お見逃しなく!

※記事掲載のコードはこちら。記事の補足情報はこちら

※読者アンケートはこちら

Vol.78 補足情報

投稿日:2022.05.25 | カテゴリー: コード

Raspberry Piを100%活用しよう

 「明るさセンサ拡張基板」(ADRSZLX)に搭載されている光学式センサーは、「VCNL4010」ではなく、「VCNL4020」でした。お詫びして訂正します。なお、記事内で利用している「Adafruit_VCNL40xx」ライブラリおよび、サンプルプログラムは、VCNL4020でも問題なく動作します。

情報は随時更新致します。

  • shell-mag ブログの 2022年5月 のアーカイブを表示しています。

  • -->