2024/01/17(水)mpg123とmpg321@ラズパイCUI

自分は、ラズパイなどLinuxでもGUIがないとほとんど何もできない&WindowsのようにGUIできるの最高だぜな人。
しかし、最近ラズパイをCUIだけで使い始めててちょっと楽しくなってきた。
それで、ラズパイならつけっぱなしでも気楽だし部屋のBGMに垂れ流し音楽プレイヤーにもしようと思ってCUIプレイヤー探していたときに「なんやねん」って思ったのがmpg123mpg321は別のアプリってこと。いやほんとなんやねんこれ。

最初mpg321をインストールして使ってたけどなんかプチプチ途切れたような再生で音質が悪い。それから結局mplayerという別のアプリを試したらノイズが消えて解決しました。この時点でmpg123の方の存在は知らなくてノイズの原因調べてたらmpg321はmpg123の亜種みたいなソフトでノイズが乗る現象をほかの人も書いてたことを知って、何が言いたいかというと「ねんやねんそれ」っていう文句w

まとめるとLinuxのCUIで有名な(?)プレイヤーを知った3つが以下
  • mpg123
  • mpg321(mpg123の亜種)
  • mplayer(FFmpegがエンジンなのでなんでもござれプレイヤー)
いずれも、
sudo apt-get install ほげほげ
するだけの簡単インストール。

熟成された格ゲー・音ゲーみたいに、初心者に厳しいLinuxと思う…のでそれを少しでも緩和するべく記録に残すことにした。

あ、あとubuntuでuseraddadduserで挙動が違うのは許し難いぞ…。

2024/01/12(金)Arduinoボード 3品種のLDO駆動能力

よく使うArduino3品種の3.3V出力の最大消費電流が分からなかったので調べたメモ。
搭載LDOの最大電流は下記の通り、自作の回路やシールド基板では1割程度余裕を見ておいたほうが良いと思う。
ボード搭載LDO最大電流
Arduino UNO R3LP2985-33DBVR150mA
Arduino Uno R4 Minimaマイコン内蔵LDO100mA
Arduino Nano EveryAP2112K-3.3600mA
Nano Every 結構強力、W5500 Ethernetチップなんかも余裕で動きそう。
がっつり3.3Vが必要な場合はVINから自前でDCDCで作るのが順当。

2023/08/21(月)Arduino Nano Everyの挙動

自分用のメモ。
Arduino Nano Everyになってから、UNO R3と違ってタイマ/カウンタが盛りだくさんになった。
その挙動を調べていて使ってるところ使ってないところなどの確認。

とりあえず起動時のTCA0周りを一覧
TCA0.SINGLE.INTCTRL = 0b0
TCA0.SINGLE.CTRLA = 0b1011
TCA0.SINGLE.CTRLB = 0b11
TCA0.SINGLE.CTRLC = 0b0
TCA0.SINGLE.CTRLD = 0b0
TCA0.SINGLE.CTRLECLR = 0b0
TCA0.SINGLE.CTRLESET = 0b0
TCA0.SINGLE.CTRLFCLR = 0b0
TCA0.SINGLE.CTRLFSET = 0b0
TCA0.SINGLE.EVCTRL = 0b0
TCA0.SINGLE.INTCTRL = 0b0
TCA0.SINGLE.PER = 255
TCA0.SINGLE.CMP0 = 128
TCA0.SINGLE.CMP1 = 128
TCA0.SINGLE.CMP2 = 128
TCA0.SINGLE.CTRLCなのですが、起動時は0b0だがtone()実行中は0b111になっています。
比較一致(Compare) CMP2OV CMP1OV CMP0OV 出力ですね。なぜ全部?

あと、この記事がすごく役立ちました。
@CoTechWorks さん
Arduino Nano Everyのタイマー割り込み機能について

この記事を参考に進めています。

2022/08/20(土)Nordic SoftDevice S132 v17.1.0にSerializeFWでアクセス

NordicのnRF52832を積んだマイコンは各社から出てますが、大体がそのチップ自体のプログラミングをして動かしてると思う。
NordicはSerialization FWというのを提供しており、これはSoftDeviceと一緒に書き込むことでそのチップはBLEモジュールとして扱い(Connectivity Chipと言う)ホストMCU側(Application Chipと言う)からUARTを通してすべて操作できるというものです。

となると、UART通信仕様が必要になりますし通信フォーマットが知りたいのですが、なんとこの情報がまともに掲載されていません。v11.0.0のバージョンに無くはないのですが
https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v11.0.0%2Fble_serialization_s130_functions.ble_serialization_s130_functions
命令バイトとなるopcodeがv17系と違っていてバージョン違いで平気で仕様を変えてきます。(なんなんですかこれ…。)
調べているなかで本家のコミュニティのDevZoneに同じ悩みの人がいたのですが、誰も知らないみたいなまともな回答がなくもうなんだか…。(なんなんですかこれ…。2回目)

SDKの中を覗いてみると
nRF5_SDK_17.1.0_ddde560\components\serialization
というのがあって、どうやらMCU側はこのライブラリを使って「よろしく動かしましょう。通信プロトコルやパケットフォーマットは気にしなくてよいです。」ということが言いたそうな空気を感じました。

でも自分は、生の通信を見ながらある程度の動きを把握したいので、解析を頑張りました。
  • TX_RAW_PACKET = [TX_HEADER][TX_PAYLOAD] の構成
[TX_HEADER]は2byteで、パケットの長さを示す[0x03, 0x00]の場合 0x0003で3byte続く
[TX_PAYLOAD]の1byte目は、0x00がMCU→BLEで0x01がMCU←BLEの方向、UARTの場合TX,RXで分かれているので各デバイスからは固定で送出されますね
2byte目はcommand code(opcode)、これがバージョンによって変わる。変わるなよ。例えばv17でsd_ble_version_get = 0x65ですが、v11ではsd_ble_version_get = 0x66です。
3byte目以降はopecodeによります。
*** sd_ble_version_get
--> 03 00 00 65 00 
<-- 06 00 01 65 10 00 00 00 

上記を送るとこんな感じで帰ってきます。10 00 00 00が返信内容でBLEからは4byteが標準的に返ってきます。あとは命令によって様々。
まずは基本はこんな通信みたいです。

肝心のv17.1.0のopcodeですが
\nRF5_SDK_17.1.0_ddde560\components\serialization\common\ser_dbg_sd_str.c
の中にありました。どうやらこれが一覧っぽいです。
S132 v17.1.0

static const char * sd_functions[] = {
    /* 0x60 offset */
    "SD_BLE_ENABLE",                           /*0x60*/
    "SD_BLE_EVT_GET",                          /*0x61*/
    "SD_BLE_UUID_VS_ADD",                      /*0x62*/
    "SD_BLE_UUID_DECODE",                      /*0x63*/
    "SD_BLE_UUID_ENCODE",                      /*0x64*/
    "SD_BLE_VERSION_GET",                      /*0x65*/
    "SD_BLE_USER_MEM_REPLY",                   /*0x66*/
    "SD_BLE_OPT_SET",                          /*0x67*/
    "SD_BLE_OPT_GET",                          /*0x68*/
    "SD_BLE_CFG_SET",                          /*0x69*/
    "SD_BLE_UUID_VS_REMOVE",                   /*0x6A*/
    "SD_UNKNOWN",                              /*0x6B*/
...
抜粋。

全然まとまらない。もう自分用備忘録ということで。

あと、UARTは、速度1Mですが、みんな115200に変更して書き込んでいる模様。
それと、パリティありって、Odd/Evenどっちなのか不明確でしたが、Evenのようです。RTS/CTSも標準で必要。
Arduino風に書くならSERIAL_8E1 | SERIAL_RTSCTSです。

【訂正・追記】2022/8/21

2022/05/31(火)Processing AndroidModeのcontrolP5ボタン

最近Processingを始めましたが、AndroidModeっていうのがあり、これを使うとProcessingアプリがAndroidスマホで動作します。どちらかというと簡易的にAndroidアプリをProcessingで製作出来る、といった方が分かりやすいかもしれません。そこで早速何か簡単な自分用ツールを作ってみようと思ったのですがいきなり躓きました。
それは、定番なProcessingGUIライブラリのcontrolP5ライブラリをAndroidModeで利用する場合、ボタンイベントが所望の動作にならないというものです。ざっと調べた結果仕様っぽかったので、手軽に使いたい自分には悲しい状況になりました。

どうやらボタンイベントに関する挙動で、マウスXY/クリックによるボタン操作と画面タップによるボタン操作の違いに起因してるようです。
他の方法としては正攻法なAndroidSDKのコンポーネントを使うだとかあるみたいなのですが、難しいことは分かりませんし、カジュアルに使えてアプリ作っていきたいので今回簡易的にボタンコンポーネントクラスを作りました。
  • TapButton.pde
    public class TapButton
    {
      float x;
      float y;
      float w;
      float h;
      String s;
      int fontsize;
      boolean visible;
    
      TapButton(float _x, float _y, float _w, float _h, String _s)
      {
        x = _x;
        y = _y;
        w = _w;
        h = _h;
        s = _s;
        fontsize = int(w / s.length());
        visible = true;
      }
    
      void Draw()
      {
        if (visible)
        {
          rectMode(CENTER);
          textAlign(CENTER, CENTER);
          textSize(fontsize);
    
          fill(255);
          rect(x, y, w, h);
          fill(0);
          text(s, x, y, w, h);
        }
      }
    
      void Visible(boolean b)
      {
        visible = b;
      }
    
      boolean Tapped(TouchEvent touchEvent)
      {
        int tapNum = touchEvent . getNumPointers( ) ;
        float tap_x, tap_y;
        if (0 < tapNum)
        {
          tap_x = touchEvent . getPointer( 0 ).x ;
          tap_y = touchEvent . getPointer( 0 ).y ;
          if ( abs(tap_x - this.x) < (w/2) && abs(tap_y - this.y) < (h/2) && this.visible)
          {
            return true;
          }
        }
        return false;
      }
    }
    
    

  • Test.pde
    import android.os.Bundle;
    import android.view.WindowManager;
    
    static int LetterBox = 16; //Letterbox of application draw area
    int sizeW, sizeH;
    boolean flag_backpress = false;//back button press flag
    boolean flag_exit = false;
    
    TapButton tb1 = new TapButton(500, 400, 300, 100, "button1");
    TapButton tb2 = new TapButton(500, 600, 300, 100, "button2");
    
    public void settings()
    {
      sizeW = displayWidth - LetterBox;
      sizeH = displayHeight - LetterBox;
      size(sizeW, sizeH);
    }
    
    public void setup()
    {
    
      frameRate(30);
    }
    
    void backPressed()
    {
      flag_backpress = true;
    }
    
    public void draw()
    {
    
      //DRAWING PROCESS --------------------------------------------------------------
      background(150);
    
      if (mousePressed)
      {
        textSize(50);
        fill(0);
        textAlign(TOP, LEFT);
        text(Integer.toString(mouseX) + "," + Integer.toString(mouseY), 0, 500);
      }
    
      textSize(32);
      fill(0);
      textAlign(TOP, LEFT);
      text(Integer.toString(displayWidth), 0, 150);
      text(Integer.toString(displayHeight), 300, 150);
    
      ellipseMode(CENTER);  // Set ellipseMode to CENTER
      fill(100);  // Set fill to gray
      ellipse(mouseX, mouseY, 100, 100);  // Draw gray ellipse using CENTER mode
    
      //TapButton draw ---------------------------------------------------------------
      tb1.Draw();
      tb2.Draw();
    
      //MAIN LOOP PROCESS ------------------------------------------------------------
      if (flag_backpress)
      {
        flag_backpress = false;
        textAlign(CENTER, CENTER);
        textSize(100);
        fill(255);
        rect(0, sizeH/2 - 80, sizeW, 160, 32);
        fill(0);
        text("Exiting...", sizeW/2, sizeH/2);
        flag_exit = true;
      } else if (flag_exit)
      {
        delay(1000);
        exit();
      }
    }
    
    public void touchStarted(TouchEvent touchEvent)
    {
      int tapNum = touchEvent . getNumPointers( ) ;
      float x = touchEvent . getPointer( 0 ).x ;
      float y = touchEvent . getPointer( 0 ).y ;
    
      //TapButton
      if (tb1.Tapped(touchEvent))
      {
        tb1.Visible(false);
      }
      if (tb2.Tapped(touchEvent))
      {
        tb1.Visible(true);
      }
    }
    
    
テストプログラムは、button1を押すと、button1が消えてbutton2を押すとbutton1が表示されます。
グローバル変数の
TapButton tb1 = new TapButton(500, 400, 300, 100, "button1");
でボタンを定義して、Draw()内の
tb1.Draw();
で描画、タップイベントは
public void touchStarted(TouchEvent touchEvent)内で、
  if (tb1.Tapped(touchEvent))
  {
    tb1.Visible(false);
  }
のようにifで判定してタップ時の処理を書きます。
(ソースコード一部修正:2022/6/12)