技術メモ

プログラミングとか電子工作とか

YouTuber(VTuber)さんのライブ配信予定をGoogleカレンダーと自動同期する

自分は二次元おたくでありますが故、VTuberさんのライブ配信などを見ながら作業することがぼちぼちあります。
基本アーカイブ勢ではあるのですが、晩酌配信のような配信はリアルタイムで楽しみたいなぁというモチベーションがあります。
可能であればその予定がGoogleカレンダーに入っていると嬉しいです。

というわけで、YouTube上の特定チャンネルの予定をGoogleカレンダーに登録するスクリプトを書いていきます。
「プログラミングとか知らねえよ!」って方でもコピペすればたぶんできると思います。
とはいえ基本はパソコンで作業する用に書いてますのでスマホのみの方は参考程度に・・・

まずは例でも見て下さい。(みる時間によっては何も入ってないかも)

calendar.google.com

このカレンダーのiCal形式URLは https://calendar.google.com/calendar/ical/0fb4lch7recm0p1oktj9k6rh28%40group.calendar.google.com/public/basic.ics となっています。
もし自分のカレンダーに取り込んでみたい、という方は上記URLを「URLで追加する」を参照しながら追加してみて下さい。

※ただし、上記のカレンダーの動作を保証するものでは御座いませんので何卒・・・

この記事では上記のカレンダーを自分で作成する方法を紹介します。
例に雪花ラミィさんを取り上げているのは、完全に自分の趣味です。
(朝活で定期的に枠が立ってて確認しやすいので)

必要情報を揃える

なにはともあれ必要な情報を集めるところからです。
まずはチャンネルのIDが必要になるので、カレンダーに表示したいチャンネルを開きます。

f:id:ysmn_deus:20200819172945p:plain
kawaii

参考: Lamy Ch. 雪花ラミィ - YouTube

雪花ラミィさんのチャンネルのURLが https://www.youtube.com/channel/UCFKOVgVbGmX65RxO3EtH3iw となっています。このURLの最後にある UCFKOVgVbGmX65RxO3EtH3iw がチャンネルのIDです。

他にはYouTube Data APIを利用する為のキーが必要なのですが、コレに関しては後述します。

Googleアカウント

そもそもアカウントがない人がこの記事を見てることは少ないと思いますが、もし持っていなければ作成して下さい。
作成する方法は解説しませんが、こちらが参考になるかと。

office-hack.com

カレンダーを作成するアカウント

「カレンダーを公開していろんな人に共有したい!」という人は新規作成して、そのアカウントでカレンダーを作成することをオススメします。

f:id:ysmn_deus:20200819171800j:plain
共有したカレンダーには作成者の名前が表示される

YouTube Data APIAPIキーを作成する

別記事にまとめました。とりあえずこれは実行して下さい。

www.tech-note.info

更新するカレンダーを新規作成する

基本的に自動で予定が更新されるようにしますが、最初は手動でカレンダーをつくってあげる必要があります。
まずは一個作りましょう。Googleカレンダーを作成するアカウントにログインした状態でGoogleカレンダーのページを開きます。

f:id:ysmn_deus:20200819185246p:plain

左下の「他のカレンダー」と書かれた右にある「+」から「新しいカレンダーを作成」をクリックします。

f:id:ysmn_deus:20200819185623p:plain

「新しいカレンダーを作成」というページに移動します。「名前」に適当な名前を付けて「カレンダーを作成」をクリックします。
ここでは「雪花ラミィ配信予定」としました。
作成が完了したらブラウザ下部に「作成が完了しました」と表示されます。ページが自動で移動しないので、前のページに戻りましょう。

カレンダーIDをひかえておく

後に「カレンダーID」が必要になってきますので、それを確認します。
Googleカレンダーのページに先ほど追加した名前でカレンダーが追加されていると思いますので、そちらの右側に出てくる設定アイコンをクリックすると「設定と共有」という項目が出てきます。

f:id:ysmn_deus:20200819191509p:plain

f:id:ysmn_deus:20200819191359p:plain

カレンダーの設定画面に移動した後に、スクロール(もしくは左にある目次から移動)すると「カレンダーの統合」という項目があると思います。

f:id:ysmn_deus:20200820014429p:plain

そこにある「カレンダーID」と書かれた下にあるメールアドレスみたいなのがカレンダーIDです。
ここでは 0fb4lch7recm0p1oktj9k6rh28@group.calendar.google.com が該当します。
これを記録しておいて下さい。あとで使います。

Google Apps Scriptで自動同期する

こっからちょっとプログラミング要素が入りますが、最悪コピペでいいとは思います。

Google Apps Scriptのコンソール

GoogleカレンダーからスクリプトをくっつけたりできないのでGoogle Apps Scriptのコンソールにプロジェクトを作成して自動実行します。
なんのこっちゃ?という方はとりあえず下記の流れに沿って操作して下さい。

まずは下記のURLからコンソールを開きます。

www.google.com

このとき、先ほどカレンダーを作成したアカウントでログインされていることを確認して下さい。
別のアカウントからでは基本うまくいきません。

f:id:ysmn_deus:20200819190029p:plain

「Start Scripting」と書かれたボタンをクリックします。
Google Apps Scriptのコンソールに移動します。

f:id:ysmn_deus:20200819192843p:plain

新規プロジェクトの作成

自動でスケジュールを更新するプログラムを実行するプロジェクトを作成します。
左上の「新しいプロジェクト」と書かれたボタンをクリックします。

f:id:ysmn_deus:20200819193044p:plain

とりあえず後で修正が必要になったときに見つけやすい用に名前は付けておきます。
左上の「無題のプロジェクト」と書かれた箇所をクリックして、名前を変更します。
とりあえず自分は YouTube Live Calendar Sync としました。

f:id:ysmn_deus:20200819193400p:plain

OK押して更新すると名前が変わってると思います。

f:id:ysmn_deus:20200819193411p:plain

コーディング

詳しいことはさておきコードをはっつけておきます。

いつのまにV8実行できるようになってたんやお前

一応挙動について説明

上記のスクリプト

  1. 実行されるとYouTube Data APIからチャンネルに登録されている動画を「作成された」時間順でソートして25件取得する
  2. 上記で取得したものから、配信予定のものだけを選択(アーカイブとかは除外)
  3. 上記で選別した動画情報を取得
  4. 上記の情報からカレンダー上に予定を作成

て感じです。

変更するところ

一番上の

const APIKEY = '[APIキー]'

の箇所を、「YouTube Data APIのAPIキーを作成する」で作成したAPIキーに書き換えます。
次に

const CAKENDAR = {
  'UCFKOVgVbGmX65RxO3EtH3iw': '0fb4lch7recm0p1oktj9k6rh28@group.calendar.google.com', // 雪花ラミィ
}

UCFKOVgVbGmX65RxO3EtH3iw の箇所を「必要情報を揃える」でひかえてあるチャンネルIDに、 0fb4lch7recm0p1oktj9k6rh28@group.calendar.google.com の箇所を「カレンダーIDをひかえておく」でひかえておいたカレンダーIDにします。

複数チャンネルに対応する場合

上記のスクリプトを複数チャンネルに対応させる場合、新しくプロジェクトを作成してもいいですが

const CAKENDAR = {
  'UCFKOVgVbGmX65RxO3EtH3iw': '0fb4lch7recm0p1oktj9k6rh28@group.calendar.google.com', // 雪花ラミィ
}

の箇所に追加しても動くようにしています。
例えば

const CAKENDAR = {
  'UCFKOVgVbGmX65RxO3EtH3iw': '0fb4lch7recm0p1oktj9k6rh28@group.calendar.google.com', // 雪花ラミィ
  '[チャンネルID]': '[カレンダーID]', // 新しく追加したカレンダー
}

というように、何個も追加できます。
ただし、増やしすぎるとクォータ制限に引っかかるので「クォータ(使用量)のお話」を確認して下さい。

試しに実行してみる

コードができたら試しに実行してみましょう。
上側のボタンの虫マークの隣の項目「関数を選択」から「run」を選びます。

f:id:ysmn_deus:20200820011816p:plain

f:id:ysmn_deus:20200820011827p:plain

「run」が選択できると、三角形マークが押せるようになるので、押してみます。

f:id:ysmn_deus:20200820011840p:plain

スクリプトを実行すると認証が要るよ!(Authorization required)と表示されます。

f:id:ysmn_deus:20200819195136p:plain

「認証を許可」を押して次に進みます。

f:id:ysmn_deus:20200819195450p:plain

カレンダーを作成したアカウントを選択します。
ヤバそうな表示がでますが無視します。ヤバイのはあなたの作ったスクリプトです。
(心配な人はアカウントを分けて下さい。私が悪意のあるスーパーハッカーだったとしても、乗っ取れるのはその新しく作ったアカウントだけです。)

無視するには左下の詳細を押して

f:id:ysmn_deus:20200819195744p:plain

自分の作ったプロジェクト名の書かれた「(安全ではないページ)に移動」をクリックします。

f:id:ysmn_deus:20200819200159p:plain

一応この操作の説明をしておきますと、今作成しているプログラムがGoogleアカウントのカレンダーなどを変更していいかどうかの許可を確認しています。
右の方にあるiの○をクリックすると詳細が表示されます。気になる方は見てみると良いと思います。

とりあえず問題無ければ「許可」をクリックします。
ここまでで何か警告っぽいものが出なければ、とりあえず実行できてるはずです。
カレンダーを確認してみて下さい。

f:id:ysmn_deus:20200820012151p:plain

ライバーさんの立ててる配信予定が入っていればとりあえず問題無いと思います。

定期実行の設定

スクリプトは上記でOKですが、毎回自分で実行していてはめんどくさいこと極まりないです。
上記のスクリプトを自動で実行するように設定しておきます。

Google Apps Scriptで書いたスクリプトは標準機能で定期実行できます。
コードの編集画面にある実行ボタンの左にある時計アイコンをクリックします。

f:id:ysmn_deus:20200820092749p:plain

初めて起動するときはなんか出てきますが「ダッシュボードを表示」でモーダルを閉じます。

f:id:ysmn_deus:20200820092849p:plain

f:id:ysmn_deus:20200820093028p:plain

右下にある「トリガーを追加」ボタンを押してトリガーを追加します。

f:id:ysmn_deus:20200820093917p:plain

色々設定できます。
実行する関数は「run」を選択しといてもらえれば、後は適当に。デフォルトでは1時間おきに更新するような設定になっていますが、もし15分や30分などより細かく更新したい場合は「時間ベースのトリガーのタイプを選択」から「分ベースのタイマー」を選択して、「時間の間隔を選択(分)」を任意の分数にしてください。
時間を短くする場合はAPIの使用量制限があるので、しつこいようですが「クォータ(使用量)のお話」をご参照ください。

時間を設定できたら「保存」でトリガーを追加します。

f:id:ysmn_deus:20200820094531p:plain

こんな感じでトリガーが追加できていれば定期実行の設定完了です。
あとはGoogleさんが勝手に自動でカレンダーを更新してくれます。

更新のタイミング

1時間で設定した方は「12:00」に実行されそうなものですが、結構実行時間はまちまちです。
たぶんトリガーを設定したタイミングが10:15だと、次回実行されるのが11:15とかそんな感じになります。
まぁ、実行時間は目安だと思っといて下さい。

カレンダーを共有する

上記で作成したカレンダーを共有してライバーさんの配信を布教したい欲求もでてくると思います。
そういうときはGoogleカレンダーの共有機能を利用しましょう。
ここでは、iCal形式の公開URLを共有する方法を利用します。

一般公開の設定を行う

URL事態は既に生成されているのですが、一般公開できる設定をしていないと他の方は取り込めません。
なのでまずは一般公開できるように設定を変更します。

まずは該当カレンダーの設定画面を表示します。
カレンダーIDを確認したページです。

f:id:ysmn_deus:20200819191359p:plain

このページの「アクセス権限」を確認します。

f:id:ysmn_deus:20200820013412p:plain

該当項目の左側にあるチェックボックスをクリックしてチェックを入れます。

f:id:ysmn_deus:20200820013500p:plain

警告が出ますのでOKを押します。
これでOKです。もう少し下にスクロールして「iCal形式の公開URL」と書かれた箇所を確認します。

f:id:ysmn_deus:20200820013739p:plain

このURLでカレンダーを共有します。
このURLで追加された場合、閲覧しているユーザーは編集できないので悪戯などの被害は基本的に考慮しなくて良いかと思います。

共有リンクで共有する

上記の「アクセス権限」という項目の下に「共有可能なリンクを取得」というボタンがあります。

f:id:ysmn_deus:20200820113119p:plain

これをクリックするとURLが出てきますので、このURLにアクセスするとログインしているユーザーのGoogleカレンダーにカレンダーが追加されます。
一般的にこちらの方が楽だと思います。

URLで追加する

自分のGoogleカレンダーiCal形式のURLで追加する場合は、「他のカレンダー」の「+」ボタンから追加します。

f:id:ysmn_deus:20200820014022p:plain

「URLで追加」という項目があるので、そちらをクリックします。

f:id:ysmn_deus:20200820014137p:plain

先ほどの「iCal形式の公開URL」と書かれた箇所にあったURLを入力し、「カレンダーを追加」をクリックします。

これでOKだと思います。

注意点

クォータ(使用量)のお話

YouTube Data APIはなんぼでも使い放題というわけではなく、1ユーザーあたりに割り当てられた使用量みたいなものがあります。
いろんな制限があるのですが、関係するものとしては「1日あたり10,000ユニット」という制限が該当すると思います。
('ω')。o(????????????)という感じだと思いますが、先ほど紹介したスクリプトではおおよそ1チャンネルあたり100ユニットを消費するような仕組みになっておりますので、例えば1チャンネルを15分刻みで更新チェックすると

100 * 96(一日1440分 -> 96回)= 9600

となり、9600ユニット消費します。なので結構ギリギリです。(このユニットは毎日リセットされるので、一日あたりを考えればいいです。)
なので、2チャンネルを先ほどのスクリプトで処理すると、単純に2倍になるのでお昼のどこかでスクリプトが動かなくなります。

考えられる対策としては

  1. 更新頻度を下げる(30分で2チャンネル、1時間で4チャンネル、など)
  2. ユーザーを分ける(1ユーザー1カレンダーなど)

といった対応が考えられると思います。
ただし、ユーザーを分けるというのはGoogleの定めた制約を回避するためのサブアカウント作成と見なされる可能性があるので、基本的には前者をオススメ致します。
複数チャンネルの同期を考えている方は参考にしてみて下さい。

APIそのもののエラーの可能性

この記事で紹介した方法は基本「YouTube Data API」に依存しています。
なので、いわゆる枠バグのような現象が発生するライブに関してはもしかするとカレンダーに同期されない可能性があります。
本当にみたいライバーさんであれば、Twitterなど他媒体のチェックも欠かさないようにしましょうね。

公開カレンダー

一応自分が運用してるカレンダーを貼っておきます。
お好きにどうぞ。
(1時間に1回更新です)

YouTube Data API (v3)を利用する為のAPIキーを取得する

基本的に下記の公式リファレンスを参照すれば大丈夫なんですが、よくわからんかった!という方がいらっしゃったら参考にして下さい。

developers.google.com

Googleアカウントの作成

詳しくは解説しませんが、こちらが参考になるかと。

office-hack.com

Google開発者コンソールの認証ページを開く

YouTube Data APIGoogleの1サービスとして提供されているものです。
基本的に設定はGoogleの開発者コンソールからいろいろ設定します。下記にアクセスしてください。

console.developers.google.com

ログイン情報が求められた場合はメールアドレスとパスワードを入力してログインしましょう。
初めてコンソールにアクセスする場合は利用規約などの表示がでると思いますので、利用規約を一読しチェックを入れます。

f:id:ysmn_deus:20200819174540p:plain

その後、右下の「同意して続行」を押すとGoogle Cloudプラットフォームの「認証情報」の画面が表示されるかと思います。

f:id:ysmn_deus:20200819174731p:plain

なにはともあれプロジェクトを作成しないことには作業ができないようになっています。
まずはプロジェクトを作成しましょう。

プロジェクトの作成

左上の「プロジェクトの選択」をクリックします。

f:id:ysmn_deus:20200819175334p:plain

「プロジェクトの選択」モーダルが表示されると思いますので、右上の「新しいプロジェクト」をクリックします。

f:id:ysmn_deus:20200819175427p:plain

「新しいプロジェクト」と表示されたページに移動すると思います。
プロジェクト名に適当な名前(後で見て分かりやすい名前、ここでは YouTube Live Calendar Sync としておきますが、なんでもいいです)

f:id:ysmn_deus:20200819175725p:plain

「場所」は特に変更しなくて良いと思います。「作成」をクリックしてプロジェクトを作成します。

f:id:ysmn_deus:20200819175907p:plain
ぐるぐるがでて・・・

下記のような画面になれば、プロジェクトの作成は完了です。

f:id:ysmn_deus:20200819175925p:plain

APIキーの作成

ようやく本題です。「認証情報を作成」から「APIキー」を作成します。

f:id:ysmn_deus:20200819180512p:plain

f:id:ysmn_deus:20200819180616p:plain

あら簡単!APIキーができました。

f:id:ysmn_deus:20200819180857p:plain

上記の画像では黒で塗りつぶしていますが、なにか文字列が並んでいると思います。
これがAPIキーです。たぶんこの記事を見ている方はなにがしかに使うのでメモっておいてください。
また、基本的にこの文字列は公開しないようにしましょう。めんどくさいことになります。
一応「キーを制限」というボタンをクリックして、名前だけ変更しておきます。

f:id:ysmn_deus:20200819181204p:plain

f:id:ysmn_deus:20200819181323p:plain

YouTube Data API を有効にする

前までの項目でAPIキーは作成できてるのですが、YouTube Data APIが利用できる状況ではありません。
YouTube Data APIが利用できるようにしましょう。

同じ画面の「APIとサービスを検索します」というところに「youtube」と入力しましょう。

f:id:ysmn_deus:20200819181546p:plain

YouTube Data API v3」と書かれた箇所をクリックすると、YouTube Data API v3が表示されたページに移動すると思います。

f:id:ysmn_deus:20200819181700p:plain

「有効にする」というボタンがあるので、それをクリックします。
ぐるぐるが終わったらYouTube Data API v3の利用状況が表示されたページに移動します。

f:id:ysmn_deus:20200819181845p:plain

基本的にこれで作業は完了です。

ためしにAPIを使ってみる

APIキーがちゃんと有効になっているか確かめてみます。
ひかえてあるAPIキーで下記のURLを書き換えてブラウザでアクセスしてみて下さい。

https://www.googleapis.com/youtube/v3/search?part=id,snippet&q=youtube&key=[APIキー]

例えば、APIキーが ABCDEFG なら https://www.googleapis.com/youtube/v3/search?part=id,snippet&q=youtube&key=ABCDEFG です。

f:id:ysmn_deus:20200819182922p:plain

なにかゴチャゴチャと表示されていれば成功です。
一応上のAPIは、「youtube」というキーワードで検索したときの動画の結果になっています。
(上記の画像ではヒカキンさんの動画がヒットしている)

以上で大丈夫なはず!

f:id:ysmn_deus:20200819183342p:plain

NanoPi R2Sのリモートセットアップ(たぶんRaspberry Piとかでもいける)

所用でVPNブリッジのマシンを選定しておりまして、NanoPi R2Sである程度パフォーマンスが出ると導入しやすいし嬉しいなぁと言うことでNanoPi R2Sを複数台調達しました。
とりあえず現段階でのセットアップ方法をメモしておきます。
VPNブリッジにする記事は気が向いたら書きます。

なんでリモートでセットアップ?

NanoPi R2Sにはディスプレイ接続できないからです。
Raspberry Piもセットアップの時にディスプレイじゃまくさくないですか?

必要なもの

f:id:ysmn_deus:20200817135236j:plain

  • NanoPi R2S本体
    自分はFRIENDLY ELECから直接購入しました。
    NanoPi R2S
    1個だけ購入するなら本体代22 usd + 送料(8.9 usd ~ 27 usd、オススメはSF-Express)なので、1個だけにしろ基本的に公式から買うのが安いです。
    一応Amazonにも何種類かありますが、何故かケースありの方ケース無しより安かったのでそちらがオススメです。

  • ACアダプタ(USB電源でも可)
    よくラズパイとかとセットになっている、USB-microのACアダプタが妥当です。
    専用に購入するならこんなやつ
    こういうスマホを充電するUSB-ACアダプタでも問題無いかと思いますが、1ポートあたり2Aは確実に流せるものを準備しましょう。

  • SDカード
    今回は公式で用意されているイメージを利用しますので、少なくとも8GBぐらいは必要です。
    たぶん8GBだと詰むのでミニマム16GBのものとかでしょうか。(自分はよく32GBを利用します)
    SDカードは意外と破損しやすいので謎メーカーを避けるとして、SanDiskのもので十分かと思います。
    ちょっと心配な人なら東芝製とかがオススメです。(どっちも使ってて壊れたことないですが)

  • LANケーブル これは適宜。VPNブリッジとして利用するので少なくとも2本ぐらいはいると思います。

SDカードにイメージを書き込む

NanoPiの公式サイトから専用のイメージをダウンロードしてきます。
図の「download link」というところから保存できます。

f:id:ysmn_deus:20200817140215p:plain

BaiduとGoogle Driveの2択ありますが、無難にGoogle Driveで良いと思います。(Baidoはパスワード入力などが必要)

f:id:ysmn_deus:20200817232940p:plain

f:id:ysmn_deus:20200817232952p:plain

zipファイルが保存されるので、解凍して「rk3328-sd-friendlycore-bionic-5.4-arm64-20200707.img」を準備します。
通常のRaspberry PiであればRaspbianなどで良いと思います。 Linuxならマウントしてddコマンドでいいですが、Windowsの場合は

  • フォーマット
  • ファイルコピー

が必要になります。

フォーマット

無難にSDメモリカードフォーマッターを利用してフォーマットするのが良いと思いますが、たぶんWindowsの標準機能でフォーマットしてもいいんじゃないかと思います。

www.sdcard.org

利用方法などは上記ウェブサイトのユーザーマニュアルなどを参考にして下さい。

ファイルコピー

結構「Win32 Disk Imager」を利用している例が多かった(公式もそう)のですが、Windows10でうまく動かなかったのでUSBWriterを利用しました。

www.gigafree.net

たぶん使い方は起動すれば分かると思います。

f:id:ysmn_deus:20200817233655p:plain

上記2項目を行えば、SDカードの準備は完了です。

初期設定

とりあえず上記までで事前準備は完了なので、SDカードを挿してWAN側にLANケーブルを挿し、ルーターなどに接続します。
基本的にここまで行けばあとは全てSSH経由で行うので、電源が入っていれば手の届かない所にあっても良いと思います。

IPの確認

基本的にNanoPi R2SはDHCPでIPが割り当てられる環境にいるとします。
(普通にルーターを使っている環境であれば、どっかのポートに挿せばIPが割り当てられます)
NanoPi R2Sにはディスプレイポートなどはないので、IPはルーター側のIPリリース情報から取得します。
これは、ルーターによって異なるので割愛しますが、たぶん管理画面の「DHCP」の設定項目みたいな箇所で確認できます。
(もしくはログ?)

SSHでログイン

ちょっとここで躓きました。公式サイトには「rootでログイン」と書かれており、実際/etc/ssh/sshd_configにも

...
#LoginGraceTime 2m
PermitRootLogin yes
#StrictModes yes
...

となっているのでrootでログインできそうなもんですが、どうもうまくいきません。
(ブラウザからのアクセスもうまくいきませんでした)
とりあえず

  • ユーザー名: pi
  • パスワード: pi(通常のRaspberry Piの場合はraspberry)

でいけたので、これでSSHでログインします。
(Friendly Elecで販売されている「NanoPi」シリーズは、大体ユーザー名がpiでパスワードもpiなので、試してみたらいけました)

ssh pi@[前項目で確認したIPアドレス]

本当はとっととrootにパスワードを付けたい所ですが、念のためIPの固定からやっていきます。

IPの固定

これは色々方法(ルーター側からMACアドレスとIPを紐付けるなど)があると思いますが、とりあえず家のネットワークは基本マシン側からIPを固定しているのでNanoPi R2Sの設定で対応します。
中に乗ってるディストリはUbuntuですので、Ubuntuの公式ネットワーク設定に従います。

ネットワークインターフェースの確認

まずどのインターフェースがWANとLANに割り当てられているか見てみます。

pi@NanoPi-R2S:~$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether ba:88:4f:97:b2:26 brd ff:ff:ff:ff:ff:ff
    inet [前項目で確認したIPアドレス]/24 brd 192.168.x.255 scope global eth0
...

[前項目で確認したIPアドレス]があるインターフェースがWAN側のポートになります。
ここではeth0が該当します。

設定ファイルの作成

/etc/netplan/99_config.yamlを作成して、そこに設定を追記します。

pi@NanoPi-R2S:~$ sudo vi /etc/netplan/99_config.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: false
      addresses: [[前項目で確認したIPアドレス]/24]
      gateway4: 192.168.x.1
      nameservers:
          addresses: [192.168.x.1, 8.8.8.8, 8.8.4.4]

とりあえずGoogle Public DNS(8.8.8.8, 8.8.4.4)も適応しておきます。お好みでCloudflareにしてもいいでしょう。
ファイルを作成できたら

sudo netplan apply

で適応します。
たぶんこれでIPは変わらない筈。

パスワードの変更

とりあえずrootがパス無しなのと、デフォルトユーザーのpiがpiという脆弱極まりない環境が気になるので変更しておきます。

pi@NanoPi-R2S:~$ sudo passwd
Enter new UNIX password:  <- パスワード入力
Retype new UNIX password: <- もう一回入力
passwd: password updated successfully

一応あってるか確認

pi@NanoPi-R2S:~$ su -
Password: <- 先ほど設定したrootのパスワード入力

ユーザーが切り替わればrootのパスワードは変更完了です。
piも変更しておきます。(たぶんこっちをメインに使う事になる)

pi@NanoPi-R2S:~$ passwd
Changing password for pi.
(current) UNIX password: <- 現在のパスワード入力、今は「pi」
Enter new UNIX password: <- 新しいパスワード入力
Retype new UNIX password: <- もう一回入力
passwd: password updated successfully

ログアウトして確認しておくといいでしょう。
(現段階では最悪SSHのrootログインが有効になっているので、パスワードミスって入れなくなった場合はrootでパスワードを変更するとよさげ)

公開鍵認証でログインできるようにする

とりあえずサーバーにパスワードログインのまま放置するのは気持ち悪いので公開鍵認証でログインできるようにしておきます。
鍵の生成や詳しいことに関しては割愛します。(長くなるので)

公開鍵の追記

ログインするユーザーのホームディレクトリに.sshディレクトリを作成してauthorized_keysを追加しておきます。

pi@NanoPi-R2S:~$ mkdir .ssh
pi@NanoPi-R2S:~$ vi .ssh/authorized_keys <- 自分の公開鍵を追記
pi@NanoPi-R2S:~$ sudo chmod 600 .ssh/authorized_keys 
pi@NanoPi-R2S:~$ sudo chmod 700 .ssh/ 

SSHの設定

SSHの設定を変更しておきます。

pi@NanoPi-R2S:~$ sudo vi /etc/ssh/sshd_config

とりあえず個人的に変えておきたい場所だけ記載しておきます。

Protocol 2
Port [22以外の任意のポート]
PermitRootLogin no
PasswordAuthentication no
PermitEmptyPasswords no
#X11Forwarding yes <- デフォルト値はnoなのでコメントアウト

上記を反映させるためにsshをリスタートします。

pi@NanoPi-R2S:~$ sudo /etc/init.d/ssh restart

とりあえず再起動しなければSSHは繋がりっぱなしになってると思うので、今のうちに別クライアントか別窓で鍵認証でSSHログインできるか確かめます。
もしできない場合は上記の設定をしていたウィンドウで設定画面を確かめて下さい。

LPCXpresso4337のCortex-M4/Cortex-M0マルチコアのサンプルプロジェクトをビルド+書き込みしてみる

お恥ずかしいことにLPC4337を使ったボードをよく開発しているにもかかわらず、マルチコア開発はしたことがありませんでした。
ただ最近になってマルチコアの需要がでてきた(とはいえ要求スペック的にはシングル処理でも間に合ってはいるんですが・・・)のでマルチコア開発の一歩としてサンプルプロジェクトをビルド+書き込みしてみます。

f:id:ysmn_deus:20200612183508j:plain

環境

前提としては

  • 使用するマイコンはLPC4337で、サンプルなのでボードはLPCXpresso4337を使用する
  • 上記につき、デバッガプローブはボード上にあるLPC-Link2相当のものをそのまま利用する
  • 開発IDEはMCUXpresso IDE v11.1.1

という感じです。
LPC-Link2を使ってもいいんですが、まぁ最初は軽い気持ちでやりたいので。

プロジェクトの作成

LPCOpenからインポート

NXP製のマイコンをいじるならいまだとLPCOpenを使って開発することが多いと思います。
ここでは郷に従ってLPCOpenからライブラリとサンプルプロジェクトをインポートします。

MCUXpressoのおそらく左下にある「Quickstart Panel」から「Import projetc(s) from file system...」を選択します。

f:id:ysmn_deus:20200612171407p:plain

出てくるウィンドウの「Project archive (zip)」から「Browse」でLPCOpenのzipファイルを選択します。

f:id:ysmn_deus:20200612171453p:plain

f:id:ysmn_deus:20200612171739p:plain

f:id:ysmn_deus:20200612171749p:plain

f:id:ysmn_deus:20200612171759p:plain

今回はマルチコアのLチカのみ利用するので、LPC43xxのチップ用のプロジェクト(lpc_chip_43xx(m0))とLPCXpressoボード情報のプロジェクト(lpc_board_nxp_lpcxpresso_4337(m0))とマルチコアのサンプルプロジェクト(multicore_mc_sa_blinky_m4(0app))の6プロジェクトをインポートします。

f:id:ysmn_deus:20200612172257p:plain

「Finish」を選択すればIDEに戻り、Project Explorerにプロジェクトが6つ増えていると思います。

f:id:ysmn_deus:20200612172411p:plain

プロジェクトの設定を調整する

チップとボードのプロジェクトには手直しが必要ありませんが、サンプルプロジェクトはそのままではビルドが通らないのでマルチコアの設定を行っていきます。
最初にCortex-M0側から設定します。

Cortex-M0側のプロジェクト(Slave)

メモリ設定

こちらから設定するのは、Cortex-M4のプロジェクトでこちらのビルド済みオブジェクトファイルを指定する必要があるためです。
まずはプロジェクト設定を開きます。

f:id:ysmn_deus:20200612172801p:plain

C/C++ Build」→「MCU settings」の箇所を修正します。
一見「Cortex-M0」などを選択されてるのでよさそうですが、メモリ設定が適応されていないようです。
設定をインポートするため、下の方にある「Import」でメモリ設定をインポートします。

f:id:ysmn_deus:20200612173025p:plain

インポートするファイルはデフォルトの設定でIDEをインストールしている場合は「C:\nxp\MCUXpressoIDE_11.1.1_3241\ide\Wizards\MemConfigs\NXP」にある「LPC43x7-M0_BankB.xml」を読み込みます。
(MCUXpressoIDE_11.1.1_3241の箇所はバージョン毎に違います。)
メモリの設定が写真の様に変わっていればたぶん大丈夫です。(Cortex-M0のプログラム領域はLPC43x7では0x1b000000からなのでよさそう)

f:id:ysmn_deus:20200612173401p:plain

「Apply」を選択して適応しておきます。

MCU Linkerの設定

あとはMCU Linkerの設定だけしておきます。
同じ様にプロジェクトの設定で「C/C++ Build」→「Settings」→「MCU Linker」→「Multicore」を参照します。

f:id:ysmn_deus:20200612173931p:plain

デフォルトでは「None」になっていると思いますが、「M0APP」にしておきます。
設定は以上なので「Apply and Close」で設定ウィンドウを閉じておきます。

以上の設定を行い、プロジェクトのビルドをしておきます。
プロジェクトを右クリックし、「Build Project」を選択します。

f:id:ysmn_deus:20200612174159p:plain

関連プロジェクトは勝手にビルドされると思います。
問題なければCortex-M0の方のプログラムのオブジェクトが「Debug」下に生成されているかと思います。

f:id:ysmn_deus:20200612174359p:plain

ここまで出来ればCortex-M0の方は終わりです。

Cortex-M4側のプロジェクト(Master)

基本的な動作としてはCortex-M4側のプログラムが起動し、Cortex-M0を起動するという流れなので、こちらのプロジェクトがメインになります。

メモリ設定

基本的にデフォルトの設定で問題ありません。
念のためCortex-M0に割り当てるFlashの領域が0x1b000000になってるかぐらいはチェックしましょう。
該当箇所は先ほどと同様にプロジェクトの設定を開いて「C/C++ Build」→「MCU settings」です。

f:id:ysmn_deus:20200612174705p:plain

MCU Linkerの設定

こちらも上記と同様に「C/C++ Build」→「Settings」→「MCU Linker」→「Multicore」を参照します。

f:id:ysmn_deus:20200612174856p:plain

先ほどとはちょっと表示が違います。
この「M0APP」にチェックを付け、残り二項目も設定します。

f:id:ysmn_deus:20200612175120p:plain

f:id:ysmn_deus:20200612175132p:plain

設定できたら「Apply and Close」で設定を適応させてウィンドウを閉じます。

Cortex-M4側のプロジェクトのビルド

プロジェクトのビルドはこちらから行います。
上記までの設定をしていれば、通常のビルドで問題無い筈です。プロジェクトを右クリックし、「Build Project」しましょう。
M0APP execute address differs from address provided in source imageと怒られる場合はCortex-M0側のメモリ設定ができていない可能性が高いです。

問題無くビルドができれば実際にデバッガを起動して書き込んでみます。
簡単な動作なのでGUI Flash Toolを使って書き込みだけするのでも良いと思います。

f:id:ysmn_deus:20200612175705p:plain

接続しているデバッガが出てくるので、該当するデバッガを選択してOKします。

f:id:ysmn_deus:20200612175916p:plain

とりあえずマルチコアデバッグもしたいのでJTAGがいいです。

f:id:ysmn_deus:20200612180045p:plain

以上でM4の方のデバッガが起動し、書き込みまで完了しています。
ここでM0のほうもマルチコアデバッグする場合は、デフォルトのデバッグ設定に一箇所設定を追加します。
Cortex-M0側のプロジェクトを右クリックして「Debug Configurations」を開きます。

f:id:ysmn_deus:20200612181713p:plain

開かれた時にはM4のデバッグ設定しかないと思いますので、「C/C++ (NXP Semiconductors) MCU Application」をダブルクリックします。

f:id:ysmn_deus:20200612182026p:plain

ダブルクリックするとプロジェクト名でデバッグ設定が新しく追加されます。

f:id:ysmn_deus:20200612182102p:plain

このままではデバッグできないので、「Search Project」から対象ファイルを選択します。

f:id:ysmn_deus:20200612182242p:plain

更に、「LinkServer Debugger」のタブで「LinkServer Options」の「Debug Cnnection」にある「Attach only」にチェックを付けておきます。

f:id:ysmn_deus:20200612182430p:plain

まず最初にCortex-M4側のデバッグを開始してから、デバッグを停止せずにCortex-M0のデバッグ(上記で設定したやつ)を開始します。
基本的にはメニューバーの虫ボタンをクリックすればデバッグできると思いますが、Cortex-M0側のデバッグの初回は表示されてないかもしれないので再度「Debug Condiguration」を開いてみてください。たぶん先ほど設定したものがありますので、そちらを選択して「Debug」すれば大丈夫だと思います。

Debugのウィンドウに2個スレッドが表示されていればマルチコアデバッグ出来てると思います。

f:id:ysmn_deus:20200612182935p:plain

リモートワーク蔓延る世の中になったので、改めてRedmineを導入してみる

f:id:ysmn_deus:20200503142752p:plain

学生の頃に団体でのタスクを管理するためにRedmineを導入していたのですが、正直活用しきれていなかったのとPassengerのエラーにおびえていたので今まで前向きにRedmineの活用を検討してきませんでした。
(正直作業も一人で完結することが多いので・・・)
しかし、昨今の情勢に有効活用できる対象の方々が増えてるんじゃないかと思い、防備録を兼ねて記事にしてみます。

そもそもRedmineとは?

もしかしたらRedmineを知らずに見て下さっている方の為にざっくりと説明しておきますと、Redmineとはブラウザ上で利用できるプロジェクト管理アプリケーションです。
具体的な説明などは公式サイトを一読して頂ければ有り難いです。

redmine.jp

デモなんかもあります

my.redmine.jp

どういう人におすすめ?

基本的に複数人で一つのプロジェクトを進める人は活用を検討するのが良さそうです。

一人での進捗管理にも使えるかもしれませんが、Redmine特有の癖というか運用に対して色々と学ばねばならない点がありますので、正直面倒だと思います。
(本記事では余り言及しない予定です)

導入

前提

基本的に

  • 複数人で使うので外部からアクセスできるサーバーにセットアップ
  • 小規模(2~10人程度?)のアクセスを想定

という想定でセットアップします。
小規模なので今回はAWSのLightsailを選定しました。
中規模や転送量が多い場合はさくらのVPSなどもいいかもしれませんが、外向けの転送量は1TBまでは込みで3.5ドル(初月は無料)なので試してみる分にはワンコイン以下で運用できると思います。

サーバーのセットアップ

LightsailにはBitnamiによるセットアップが可能となっています。
本来であればデータベースなどセットアップがいるんですが、この機能を利用することで基本的なセットアップに関しては何も考えなくてもよくなります。

※ ただし、2021年4月以降も含む長期運用まで考えている方はLightsailのBitnamiでセットアップされる環境がUbuntuの16.04なのでバックアップとリストアの方法も合わせて確認しておいた方が良いかもしれません。

Lightsailでインスタンスの作成

Lightsailのコンソールから「インスタンスの作成」からインスタンスの作成画面に映ります。

f:id:ysmn_deus:20200502183720p:plain

「設計図の選択」のところで「Redmine」を選択します。
ロケーションは日本の方を想定しているのでデフォルトの東京リージョンから変更してません。

f:id:ysmn_deus:20200502184215p:plain

初めてLightsailを利用する方はSSHキーペアが設定されていないと思いますので新規作成して下さい。

f:id:ysmn_deus:20200502184418p:plain

インスタンスプランは一番最小の3.5USDのプランを選択してます。
リソース名は分かりやすい名前を設定しておいて下さい。(よく分からない人はデフォルトの「Redmine-1」で問題無いかとおもいます。)

f:id:ysmn_deus:20200502184623p:plain

これで「インスタンスの作成」をすると、基本的なセットアップが全て完了します。
なんと便利な・・・

f:id:ysmn_deus:20200502185038p:plain

ホームに先ほど設定したリソース名のインスタンスが作成されていると思います。

IPの固定

今のままだとサーバーのIPが変わって鬱陶しいです。
IPを固定してドメインを割り当てる(割り当てなくても利用はできますが)場合にはIPを固定しておく必要があります。
LightsailでDNSゾーンを作成してドメインを割り当てる方法もありますが、今回DNSの設定は別途することとしてIPだけ固定します。

ホーム画面から作成したインスタンスの設定画面にいきます。

f:id:ysmn_deus:20200502190251p:plain

ネットワーキングのタブに移動します。

f:id:ysmn_deus:20200502190605p:plain

IPアドレス」と書いてある項目から「静的IPのアタッチ」を選択します。

f:id:ysmn_deus:20200502190905p:plain

他に静的IPのリソースが余っていない場合は「静的IPアドレスの作成」画面へ移動すると思います。
ロケーションはそのままで、インスタンスは先ほど設定したリソース名(ここでは「Redmine-1」)を選択して、「静的IPの指定」と書かれた項目のリソース名は分かりやすい名前を付けておきます。
画像では「StaticIp-1」となっていますが、「Redmine-1-StaticIp」などRedmine-1に利用している事を明示しておくのが良いと思います。

f:id:ysmn_deus:20200502191238p:plain

「作成」を選択すると静的IPが作成完了します。
とりあえずホームに戻り、先ほどと同様「Redmine-1」の設定画面の「ネットワーキング」を確認します。

f:id:ysmn_deus:20200502191521p:plain

画像のように「静的IPのデタッチ」が表示されていれば、IPの固定は完了しています。

IPへドメインを設定する

ドメインを何で運用しているかによりますが、基本的にDNSレコードにAレコードとして該当するIPを設定すると良いかと思います。
自分はRoute53を利用していますが、基本的になんでもいいとは思います。
(要望がありましたら記事化しますが、ここでは割愛します。)

基本的にHTTPSでの運用を考えてない人はココまでで問題ないかと思います。
(基本設定は公式サイトのドキュメントなどをご参照ください。)

redmine.jp

Redmineのセットアップ

管理者ユーザーでログイン

とりあえずHTTPS化したいのでその準備をします。
まずは管理ユーザでログインします。ドメインを指定した場合はそのドメインに、指定していない場合は http://[固定したIPアドレス]/ にアクセスして下さい。

f:id:ysmn_deus:20200503134438p:plain

たぶんこんな画面が表示されると思います。
右上の「ログイン」からログインします。
上記のようにBitnamiの機能でセットアップした場合は、ログインIDは「user」、パスワードはSSHでログインしたホームディレクトリにある"bitnami_application_password"というファイルに記載されているものを利用します。
Windows10であれば、コマンドプロンプトからSSHが使えますので、下記で取得可能です。
(キーペアはユーザーフォルダC:\Users\ユーザー名の直下の.sshというディレクトリに保存したとします。)

C:\Users\username> ssh bitnami@[設定したドメインor固定したIPアドレス] -i .\.ssh\[保存したキーペア].pem
$ cat bitnami_application_password

取得したパスワードでログインします。

f:id:ysmn_deus:20200503135614p:plain

ログインが完了すると、左上の項目に「管理」などが追加されています。
この「管理」を選択し、管理画面を表示します。

f:id:ysmn_deus:20200503135733p:plain

管理画面の「設定」を選択します。

f:id:ysmn_deus:20200503135944p:plain

この画面の「プロトコル」をHTTPからHTTPSにしておきます。

Bitnami HTTPS Configuration Toolを利用したHTTPS化

勿論この設定だけでHTTPS運用できるわけではないです。今回はLet's Encryptを活用したSSLを想定していますが、一応AWS Certificate ManagerとAmazon CloudFrontを利用した運用もできると思います。AWS信者の自分としては後者の方が安心安全な気がしますが、極力ミニマムに始めるという想定でLightsail上で完結する方法を採っています。

基本的なツールはなんと最初から入っており、一発で自動更新コマンドまで追加してくれます。
SSHでセットアップしたサーバーにログインしてコマンドを実行します。

sudo /opt/bitnami/bncert-tool

最初にドメイン名を聞かれるのでSSL化したいドメイン名を入力します。

Domain list []: redmine.hogefuga.com

次にwwwの扱いをどうするか(マルチドメインとして運用するのか否か)を聞かれるのでnでキャンセルしておきます。
(あんまりwww.redmine.hogefuga.comのような運用をしたい人はいないと思いますので)

The following domains were not included: www.hogefuga.com. Do you want to add them? [Y/n]: n

次にHTTPへのアクセスがあった場合にHTTPSにリダイレクトするか聞かれますので、yで許諾しておきます。

Enable HTTP to HTTPS redirection [Y/n]: y

次に設定のステップが表示され「これでええんか?」と聞かれますのでyで許諾しておきます。

Do you agree to these changes? [Y/n]: y

次に期限切れになりそうなときに通知が来るメールアドレスを登録しておきます。

E-mail address []: hoge@hogefuga.com

ここまで来たら基本の設定は完了です。最後に利用規約に同意するか否か聞かれるのでyで許諾します。

Do you agree to the Let's Encrypt Subscriber Agreement? [Y/n]: y

これでSSL化は完了です。
このツールは基本的に自動更新のコマンドも登録してくれています。一応確認する場合は、そのまま

crontab -e

とcronを確認するとコマンドが追加されていることが分かります。

オンラインでの外部とのやりとりが増えてきたので、GW中にチケット駆動など開発手法を学習しておきたいところです。
Redmineの運用に関してはド素人なので・・・(:3 」∠ )

3Dプリンタでマスクを作る「PITATT 3D print mask」を試してみた

普段はAirinum Urban Air Maskを利用しているのですが、正直フィルターが枯渇しそうです。
6月ぐらいに在庫復活する、と公式サイトに記載がありますがあやしいところ。
そこで、3Dプリンタで出力可能なマスク「PITATT 3D print mask」が公開されているという事で、これを試してみることに。

公式ウェブサイトからSTLファイル(インナーとアウターの2つのデータ)をダウンロードしてきて、スライサーで処理して出力するだけ!簡単!
と思っていましたが、アウターの出力がどうしてもサポート無しだと最後の方の積層で失敗するのでサポートありで出力しました。
(フィラメントは三菱ケミカルメディア Verbatimの白PLAを利用しました。今は白はないみたいです。)

f:id:ysmn_deus:20200418161431j:plain
実際に出力したマスク

このマスクは2個の部品に分かれており、インナーを外すことでフィルターを交換することができます。

f:id:ysmn_deus:20200418161609j:plain

フィルターはティッシュやガーゼを使って下さいとのこと。
この辺ならまだ手に入るのでありがたいです。

想像してたよりきっちり密着します。
相当試行錯誤したんではないでしょうか?頭が下がります。

医療従事者や専門職の方には不適だと思いますが、それ以外の方の応急対応としてはありだと思います。
コレを機に3Dプリンタを購入してみたいな~なんて思う人は、少なくとも150mm x 100mm x 100mm の造形エリアが確保できる3Dプリンタを購入する事をオススメします。
(最近は造形エリア広いのでだいたい大丈夫だと思いますが)

AWS Lambdaのローカル開発で環境変数を取り扱うとき(SAM テンプレート利用)

f:id:ysmn_deus:20190318182949p:plain

Lambdaで他サービスのAPIを度々利用したくなると思います。
(例えば、S3にアクセスしたり、DynamoDBからデータを取り出したり等)
ローカルでは権限を設定したIAMユーザーの認証情報を利用して実行し、運用時にはロールをLambdaに設定して走らせたいところです。
TypeScriptのwebpackでのコンパイル分岐の為にdotenvを利用しているので、そこに設定してもいいんですが、SAMのテンプレートに記述する方法もあるのでそちらをまとめておきます。

テンプレートを分ける

開発環境と運用環境などを分けたくなるので、テンプレートと環境変数ファイルを複数用意したいです。
なのでディレクトリ(もしくはファイル)で分けておきます。
具体的には

プロジェクトディレクト
├ sam/
│├ develop/ ←開発用ディレクト
││├ env.json
││└ template.yaml
│├ test/ ←自動テスト用ディレクト
││├ env.json
││└ template.yaml
│└ deploy/ ←運用版ディレクト
│ ├ env.json
│ └ template.yaml
├ built/ ←TypeScriptのコンパイル
├・・・

のようなイメージです。
(全てに環境変数が要るかは状況次第だとは思いますが)
これでとりあえず.gitignore**/env.jsonとでも追記しておけばリポジトリにヤバイ情報をアップロードしなくて済みそうです。

テンプレートの修正

ここで、SAMのテンプレートファイルを修正しておきます。
テンプレートのResources関数名PropertiesCodeUriは、テンプレートからみた相対パスなので、上記のディレクトリ構成を取っている場合は

~
    Properties:
      CodeUri: ../../built/
~

となります。

試しにローカル実行してみる

とりあえず色々動かしたのでローカルで実行してみます。
テンプレートを何個か使い分けるので、プロジェクトのルートディレクトリからテンプレートを明示して実行します。

sam local start-api -p 3001 -t sam/develop/template.yaml

今回はポート(3001)も明示しました。(よくReactで3000番は使ってるので)
とりあえず今は動けばOKです。

環境変数の定義

早速環境変数を設定していきます。
SAMでローカル実行する際に環境変数を使うには

  1. テンプレート上で変数を定義する
  2. 読み込むJSONを作成する

という手順が必要です。
コマンドですんなり入ってくれればいいんですが、テンプレート上で変数を定義するということをお忘れ無きよう。

テンプレート上で変数を定義する

テンプレートに変数の情報を書き込んでいきます。
環境変数の適応には2通りあり

  • Globalsに記述してResources全てで適応できる変数を定義する
  • Resourcesの関数毎に変数を定義する

のどちらかになります。
自分の記憶では、以前は両方を両立できたと思うんですが、現状どちらかを選べば片方の環境変数がうまく注入されないような気がします。
(例えば、Globalsに環境変数を定義すると、Resourcesの環境変数がうまく変更できない)
基本的には公式ドキュメントで例のある後者をオススメします。

Globalsに記述してResources全てで適応できる変数を定義する

テンプレートには下記の様に記載します。

~
Globals:
  Function:
    Runtime: nodejs10.x
    Timeout: 3
    Environment:
      Variables:
        GLOBAL_ENV: DUMMY
~

注入する環境変数JSONファイルは下記のように作成します。

{
  "Parameters": {
    "GLOBAL_ENV": "hoge"
  }
}

Resourcesの関数毎に変数を定義する

テンプレートには下記の様に記載します。

~
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: ../../built/
      Handler: development.lambdaHandler
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
      Environment:
        Variables:
          FUNC_ENV: DUMMY
~

注入する環境変数JSONファイルは下記のように作成します。

{
  "HelloWorldFunction": {
    "FUNC_ENV": "fuga"
  }
}

環境変数を注入して実行

上記の2通りどちらでも実行方法は同じです。

sam local start-api -p 3001 -t sam/develop/template.yaml -n sam/develop/env.json

上記のコマンドをpackage.jsonに書いておけば、簡単にテストできます。
自分はnpm-run-allを利用してSAMのローカルサーバーとwebpackのwatchを走らせてますのでpackage.jsonscripts

~
  "scripts": {
    "dev:localhost": "sam local start-api -p 3001 -t sam/develop/template.yaml -n sam/develop/env.json",
    "dev:watch": "webpack --watch",
    "dev": "run-p dev:*",
~

としておけば、yarn dev でSAMとwebpackが走ります。