Linux上でサンドボックスを使用する

Linux上でもサンドボックスを使用してブラウザを使えないか調べてみると、firejailというサンドボックスプログラムがあることがわかりました。GUIツールとして、firetoolsというのもあるようです。

早速、Synapticでfiretoolsを検索して選択すると、依存関係でfirejailも対象になりました。

アプリケーションとしてFiretoolsがインストールされたので起動すると、

ということで、ランチャが表示されました。左上隅のアイコンをダブルクリックすると、

ということで、サンドボックス内で実行されているアプリが表示されるようです。

Firefoxのアイコンをダブルクリックすると、Firefoxが起動して上記のサンドボックスの一覧表に追加されますが、タイトルバーには「スーパーユーザーで実行」という表示がついています。ただ、psコマンドで見ると、ユーザー権限で起動しているようなので、表示上のことだけなのかもしれません。

一方、Chromeの方はサンドボックスの一覧表にも追加されません。きちんと動作していないのか、それともなんなのかはよくわかりません。

とりあえず、導入だけは簡単そうなのですが、きちんと動作しているかはよくわからない状況です。

RTL-SDRを動かしてみました

これ

でポチったRTL-SDRが届きました。買ったのは

“RTL-SDRを動かしてみました” の続きを読む

SCD30のキャリブレーション

こちらのCO2センサの比較記事にSCD30のキャリブレーションはどうやるの?というご質問をいただきましたので、この記事を書いてみました。

まず、SCD30に関する情報源はSensirion社のサイトのダウンロードセンターになります。

そして、その下半分に各種資料が掲載されています。
キャリブレーションに関しては、Application Notesの中のField Calibration CO2 Sensor SCD30という資料になります。

これを見ると、冒頭のPrefaceの部分でキャリブレーションの方法としてASC (automatic self-calibration) と FRC(forced re-calibration) の2種類が存在し、この資料でそのアルゴリズムが解説されていることが説明されています。そして、これらはSCD30を組み込んだ製品の製造ラインでFRC(forced re-calibration)で初期キャリブレーションを行い、使用中のドリフト対策としてASC (automatic self-calibration)を使うことが想定されているようです。

FRC(forced re-calibration)について

forced re-calibrationは強制キャリブレーションで、既知の安定したCO2濃度の環境にしばらく置いてから、現在の(既知の)CO2濃度値をSCD30に書き込むというのが基本的な考え方です。

そして、FRC(forced re-calibration)を製造ラインで使う場合には密閉された400ppm〜2000ppmの安定したCO2濃度の環境に2分以上置いてから現在のCO2濃度値を書き込むことが推奨されているようです。

エンドユーザーがFRC(forced re-calibration)を使う場合には通常密閉されて安定したCO2濃度の環境は用意できないので、屋外に置いて屋外の環境を400ppmとみなしてCO2濃度値を書き込む方法が記載されています。

ASC (automatic self-calibration)について

対するautomatic self-calibrationは自動自己キャリブレーションで、過去の測定値からキャリブレーションを行う方法です。簡単に言えば、過去の測定値の最低値を400ppmとみなしてセンサのキャリブレーションを継続的に行うもの、と理解すればいいと思います。(この辺はMH-Z19Bのオートキャリブレーションも同じです)

したがって、ASC (automatic self-calibration)を使用する場合には、以下の条件が必要です。

  • 定期的にCO2濃度が400ppm程度まで下がること。つまり、外気との間で換気がよい人のいない環境が必要。
  • (これはSCD30ではなくMH-Z19Bの資料に記載があるのですが)CO2濃度が400ppm以下にならないこと。具体的には、森の中や温室の中など周囲に植物があり光合成が行われている環境では400ppmを下回るということです。

こういった条件があるため、SCD30ではデフォルトではASC (automatic self-calibration)はオフになっています。

実際のキャリブレーションについて

自分が使用したSparkfunのSCD30 Arduino Libraryでは、ソースコードを見ると初期化時にASC (automatic self-calibration)をオンにしています。ですので、キャリブレーションは就寝時には設置してある部屋の窓を開けて、窓際においた扇風機をONにして換気をよくする、というだけです。(センサ自体の工場出荷時にキャリブレーションされていると思うこと、濃度がはっきりわかる環境下で人が近くにいない状態でFRCを実施するのは難しいので、FRCは実施していません)

なお、SCD30ではなくMH-Z19BでオートキャリブレーションをOFFにして運用したことがありますが、やはりずれていってしまうようですので、自分の使い方の場合にはオートキャリブレーションをONにしたまま定期的に400ppm環境にさらして校正を行う(=夜間は窓を開けて換気する)必要があるのだと理解しています。

ですので、CO2モニタが設置された店舗等の場合、果たしてキャリブレーションが適正に行われているかはやや疑問です。ちなみに、横着して(冷房もありますので)1週間位締め切ったままにしていたら、

のようになりました。最後の部分は久しぶりに窓を開けて、換気をしたため急激に下がっていますが、2つのセンサの値のズレが大きくなりました。どちらが真の値かはわかりませんが、そこそこずれていきます。換気の目安としては大したズレではないですが、値の大きいところで数百ppm単位で一喜一憂することには意味がないことになります。

CircuitPythonでLPS331APを読み出す

CircuitPythonでLPS331APを読み出して気圧と温度をコンソール出力するようにしました。よく見かけるのはI2Cですが、今回はSPIで行います。

import board
import busio
import digitalio
from digitalio import DigitalInOut, Direction, Pull
import time

def rreg(add):
    result = bytearray(2)
    cs.value = False
    spi.write_readinto(bytes([add,0x00]),result)
    cs.value = True
    return result[1]    

def Pres():
    P = rreg(0xAA)              # P_H
    P = (P << 8) | rreg(0xA9)   # P_L
    P = (P << 8) | rreg(0xA8)   # P_LL
    P = P/4096.0
    return P

def Temp():
    T = rreg(0xAC)              # T_H
    T = (T << 8) | rreg(0xAB)   # T_L
    if T >= 0x8000 : T -= 0x10000
    T = 42.5 + T/480.0
    return T

# CS
cs=digitalio.DigitalInOut(board.D1)
cs.direction=digitalio.Direction.OUTPUT
cs.value=True

# SPI
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
while not spi.try_lock():
    pass
spi.configure(baudrate=10000000, phase=0, polarity=0)

# WHOAMI
print(hex(rreg(0x8F)))

# init
result = bytearray(2)
cs.value = False
spi.write_readinto(bytes([0x20,0x90]),result)   # CTRL1 REG
cs.value = True

# loop
while True:
    print(Pres(),Temp())
    time.sleep(1)

これをcode.pyとして保存すると以下のように動作します。

Code stopped by auto-reload.
ソフトリブート

オートリロードがオンです。ファイルをUSB経由で保存するだけで実行できます。REPLに入ると無効化します。

code.py 出力:
0xbb
1005.51 31.2667
1005.58 31.2833
1005.5 31.2771
1005.6 31.3104
1005.57 31.3042
1005.66 31.3083
1005.77 31.3458
1005.7 31.3438
1005.63 31.3063
1005.71 31.3417
1005.62 31.3125
1005.65 31.3146

Seeeduino XiaoでCircuitPython

せっかくなので、Seeeduino XiaoでCircuitPythonを動かしてみました。

基本的にはSeeedStudioの記事に従って操作するだけです。CircuitPythonは6.3.0の日本語版を使用しました。

記事のとおりにリセットを短く2回ショートさせて、USBドライブが見えるようになったら、ダウンロードしておいた拡張子uf2のファイルをドラッグ・アンド・ドロップで書き込みます。

その後、USBケーブルを一旦抜いて挿し直すとUSBドライブが見えるので、そこにあるcode.py(SeeedStudioの記事ではmain.pyと書いてあるがcode.pyが正しいっぽい)をエディタで開いて、

import time
import board
from digitalio import DigitalInOut, Direction

led = DigitalInOut(board.D13)
led.direction = Direction.OUTPUT

while True:
    led.value = True
    time.sleep(1)
    led.value = False
    time.sleep(1)

という内容で上書き保存すると、LEDが点滅し始めます。

このとき、

$ tail -f /dev/ttyACM0

などとしておくと、コンソール出力が観測できます。

$ screen /dev/ttyACM0

などとしておけば、対話的に操作が可能です。Ctrl+Cを押した後、リターンキーを叩くと対話モード(REPLモード)に入りますので、先のプログラムを1行ずつ打ち込めば、同様に実行できます。

Seeeduino XiaoでSPI通信

Seeeduino XiaoでSPI通信をさせてみたくて、手元に落ちていた秋月電子のLPS331APモジュール(AE-LPS331)を動かしてみました。このモジュールはすでに販売終了していて秋月電子のWebサイトには資料の掲載がなく、資料を探すのに少々苦労しました。

動作させるにあたって、LPS331APモジュール上の半田ジャンパ(I2Cのプルアップ用)を外して以下の接続を行いました。

信号名AE-LPS331ピン番号Seeeduino Xiao接続先備考
VDD1(1.71〜3.6V)3V3Xiao⇒LPS331で電源供給
SPC2SCKSPIクロック
SDI3MOSISPIデータ(Xiao⇒LPS331)
SDO4MISOSPIデータ(LPS331⇒Xiao)
CS5D1チップセレクト
GND8GNDGND

Seeeduino Xiaoの開発環境はArduino環境を使用しました。環境はSeeed Studioのページの書いてある通りにセットアップしました。(今考えれば CircuitPython で良かったのかもしれません)

LPS331APを動かすソースコードはGoogle検索で見つかった光永 法明先生のページ「ST micro のデジタル気圧センサ LPS331AP を使う(SPI接続)」という記事に掲載されているものをそのまま使わせて頂きました。(LPS331APを動かすのが目的ではなく、SPIの信号を出すのが目的なので・・・)

実際には、下記の通り、CS端子(SS端子)の設定の変更とLEDを点滅させるコードを追加しています。

#include <SPI.h>

const int LPS331AP_CS = 1;
int _SS = 1;      // ~CSをD1に接続

const byte LPS331AP_ADDR = B1011100;  // SA0 = GND
//const byte LPS331AP_ADDR = B1011101;// SA0 = VDD_IO

const byte LPS331AP_WHOAMI = 0x0f;
const byte LPS331AP_CTRL1 = 0x20;
const byte LPS331AP_CTRL2 = 0x21;
const byte LPS331AP_CTRL3 = 0x22;
const byte LPS331AP_P_LL = 0x28;
const byte LPS331AP_P_L  = 0x29;
const byte LPS331AP_P_H  = 0x2A;
const byte LPS331AP_T_L  = 0x2B;
const byte LPS331AP_T_H  = 0x2C;

const byte LPS331AP_RW = 0x80;
const byte LPS331AP_MS = 0x40;

void LPS331AP_write(byte reg, byte val)
{
  digitalWrite(LPS331AP_CS, LOW);
  SPI.transfer(reg);
  SPI.transfer(val);
  digitalWrite(LPS331AP_CS, HIGH);
}

byte LPS331AP_read(byte reg)
{
  byte ret = 0;

  digitalWrite(LPS331AP_CS, LOW);
  SPI.transfer(reg | LPS331AP_RW);
  ret = SPI.transfer(0);
  digitalWrite(LPS331AP_CS, HIGH);
  
  return ret;
}

void setup() {
  digitalWrite(_SS, HIGH);
  pinMode(_SS, OUTPUT);
  
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV8); // 8MHz/8 = 1MHz; (max 10MHz)

  Serial.begin(115200);
  while (!Serial) {}

  Serial.println(LPS331AP_read(LPS331AP_WHOAMI), HEX); // should show BB

  LPS331AP_write(LPS331AP_CTRL1, B10010000);
                             //   |||||||+ SPI Mode selection
                             //   ||||||+- DELTA_EN
                             //   |||||+-- BDU: block data update
                             //   ||||+--- DIFF_EN: interrupt circuit enable
                             //   |+++---- ODR2, ODR1, ODR0 (1Hz)
                             //   +------- PD: 0: power down, 1: active
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  long P;
  short T;
  float p, t;

  P = LPS331AP_read(LPS331AP_P_H);
  P = (P << 8) | LPS331AP_read(LPS331AP_P_L);
  P = (P << 8) | LPS331AP_read(LPS331AP_P_LL);

  T = LPS331AP_read(LPS331AP_T_H);
  T = (T << 8) | LPS331AP_read(LPS331AP_T_L);

  p = P;
  p = p/4096.0;
  
  t = T;
  t = 42.5 + t/480.0;
  
  Serial.print(P);    // pressure (reading)
  Serial.print(" ");
  Serial.print(T);    // temperature (reading)
  Serial.print(" ");
  Serial.print(p);  // pressure in [mbar]/[hPa]
  Serial.print(" ");
  Serial.println(t);  // temprerature in [`C]

  digitalWrite(LED_BUILTIN, HIGH); 
  delay(500); 
  digitalWrite(LED_BUILTIN, LOW); 
  delay(500);  
}

で、動かしてみたのですが、理由がよくわかりませんが、Arduino環境のシリアルモニタでは1秒程度で表示が止まってしまいます。不思議なことに、

$ tail -f /dev/ttyACM0 
4124716 -5507 1007.01 31.03
4124388 -5508 1006.93 31.02
4124483 -5508 1006.95 31.02
4124639 -5496 1006.99 31.05
4124199 -5532 1006.88 30.98
4124532 -5506 1006.97 31.03
4124496 -5508 1006.96 31.02
  :

とすると、問題なく動作し続けるのですが・・・。
これに嵌って、余計な時間を費やしてしまいました。

Tang Nanoで内部信号のモニタリング

GOWIN FPGA DesignerにはGOWIN Analyzer Oscilloscopeという機能があって、内部信号をモニタできます。Qiitaのこちらの記事で@hi631さんがまとめていただいているので、そちらを参考にLCD表示時の内部信号をモニタしてみました。

GAO Config Fileの作成と論理合成

新規ファイル作成でGAO Config Fileを選択する。

TypeはGowinSynthesisを使っている場合にはRTLを選択できます。Synplify Proの場合は合成後のネットリストに対してのみ可能なようです。今回はRTLに対して設定してみます。また、最初なのでトリガ条件の設定できないLiteの方でやってみます。

適当に設定ファイル名を付けます。

ファイルの一番下にGAOの設定ファイルが追加された。

これをダブルクリックして開く。

Addをクリックして観測対象の信号を選択する。Nameに信号名の一部を入力してSearchを押すと検索してくれます。

CTRLを押しながら選択することで対象信号を複数選べる。今回はLCDの外部信号を選定して、OKをクリック。

・・・と思ったが、LCD_CLKはLCDのクロック信号なので、この後除外しました。
Sample Clockの部分に同様の方法でLCD_CLKを選択。

こんな感じに設定できたところで、フロッピーのアイコンをクリックして保存。

あとは、普通にDesignペインのProcessタブで論理合成(Synthesize)と配置配線(Place & Route)をダブルクリックして配置配線まで実行。

Analyzer Oscilloscopeの実行

Tools⇒Gowin Analyzer OscilloscopeでAnalyzer Oscilloscopeを起動します。

Enable Programmerにチェックを入れます。

プログラマーの一部のような表示が出るので、Fs Fileを確認(別のプロジェクトになってることがあったような気がする)して、書き込みボタン(マウスカーソルがあるところ)をクリック。

プログレスバーが表示されて、書き込みが行われ、上の赤かった部分が緑で「Ready to acquire」に変わります。

その右側の再生ボタンをクリックしてキャプチャされるとロジアナ風の表示が出ます。

繰り返しボタンをクリックすると連続でキャプチャが行われます。Tang Nanoに載っているのは小さいデバイスなので苦しいですが、これ、簡単で便利ですね。

GowinSynthesisを使っている場合にはRTLに対して指定できるので、ソース修正⇒論理合成⇒配置配線までやったら、Analyzer OscilloscopeのTriggerタブをクリックして書き込みボタンをクリックするとダウンロードされて、再びキャプチャができるようになります。(合成後のネットリスト指定でも同様にできるのかは試していません)

Tang Nanoで液晶表示テスト

GitHubにTang Nanoのサンプルがありましたので試してみました。

このサンプルの中のexample_lcd/lcd_pjtディレクトリの下のlcd_pjt.gprjをGOWIN_FPGA Designerで開いて、論理合成と配置配線、ダウンロードを行うと、なんとなく動きますが、接続したLCDが480×272のものでしたのでちらつきます。

そこで、ソースコードを覗くとVGAod.vの中に、480×272液晶用のパラメータがコメントアウトされていました。こちらを有効化し、元の800×480のパラメータをコメントアウトしました。

これで合成、配置配線、ダウンロードすると、チラツキがなくなりましたが、800×480のつもりで表示しているので部分的にしか表示されません。そこで、このソースの最後の方に出力するラインによってRGBの出力を決めている箇所がありましたので、これを以下のように修正しました。

    assign  LCD_R   =   (PixelCount< 72)? 5'b11111 : 
                        (PixelCount< 96 ? 5'b00000 : 
                        (PixelCount<120 ? 5'b00001 :    
                        (PixelCount<144 ? 5'b00010 :    
                        (PixelCount<168 ? 5'b00100 :    
                        (PixelCount<192 ? 5'b01000 :    
                        (PixelCount<216 ? 5'b10000 :  5'b00000 ))))));

    assign  LCD_G   =   (PixelCount< 72)? 6'b111111 : 
                        (PixelCount<240 ? 6'b000000 : 
                        (PixelCount<264 ? 6'b000001 :    
                        (PixelCount<288 ? 6'b000010 :    
                        (PixelCount<312 ? 6'b000100 :    
                        (PixelCount<336 ? 6'b001000 :    
                        (PixelCount<360 ? 6'b010000 :  
                        (PixelCount<384 ? 6'b100000 : 6'b000000 )))))));

    assign  LCD_B   =   (PixelCount< 72)? 5'b11111 : 
                        (PixelCount<408 ? 5'b00000 : 
                        (PixelCount<432 ? 5'b00001 :    
                        (PixelCount<456 ? 5'b00010 :    
                        (PixelCount<480 ? 5'b00100 :    
                        (PixelCount<504 ? 5'b01000 :    
                        (PixelCount<528 ? 5'b10000 :  5'b00000 ))))));

これで合成&配置配線&ダウンロードすると以下のようにきれいに表示できました。

液晶モジュールの仕様書