忘備録-備忘録

技術的な備忘録

Arduinoシリアルから一行入力

2021-11-12 21:18:08 | arduino
Arduinoのシリアル通信で"\n"まで1行受信するプログラムです。関数loop(){}内で毎回呼び出します。受信していない場合はブロックせずに終了して戻り値が-1になります。また、"\n"まで受信していない場合も戻り値-1で終了します。

プログラム
#include <errno.h>

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

void loop()
{
  int cmd;

  if (Serial.available() > 0) {   // 受信したデータが存在する
    int rdata = receiveCmd();     // コマンドを受信
    if (rdata != -1) cmd = rdata; // -1 受信中 or 受信エラー
  }

  delay(100);
}

int receiveCmd(void)
{
  int cmd = -1;           // -1 受信中 or 受信エラー
  static int recptr = 0;  //受信した文字数を保存する
  static char recv[32];   //受信した文字列を保存する

  while (1) {             //すべての受信データを受け取る
    int receive = Serial.read();
    if (receive == -1) break;        //受信データなし ループを抜ける
    recv[recptr] = receive;
    if ((recv[recptr] == '\n') || (recv[recptr] == '\r')) {
      recv[recptr] = '\0';
      recptr = 0;
      char *ptr;
      long rcvdata = strtol(recv, &ptr, 10); //文字列を整数に変換
      if (recv[0] == '\0') break;            //空行
      if (errno != 0  )  break;              //エラー発生
      if (*ptr != '\0')  break;              //エラー発生
      cmd = rcvdata;
    } else {
      recptr++;
      if (recptr > 30) recptr = 0;           //バッファーオーバーフロー対策
    }
  }
  return cmd;
}


ESP32でインプットキャプチャ

2021-11-11 18:43:35 | arduino
ESP32マイコンを使いインプットキャプチャ機能を使ってみました。

ソースコード
#include "driver/mcpwm.h"

void setup() {
  Serial.begin(115200);
  delay(500);
  mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_0, 37);
  mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, MCPWM_POS_EDGE, 0); 
}

void loop() {
  static unsigned int old = 0;
  unsigned int number1 =  mcpwm_capture_signal_get_value(MCPWM_UNIT_0,MCPWM_SELECT_CAP0);
  if(old != number1) {
    Serial.print(number1);
    Serial.print("   ");
    Serial.println(number1 - old);
    old = number1;
  }
  delay(10);
}	

解説
インクルードファイル
#include "driver/mcpwm.h"
が必要です。
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_0, 37);
でインプットキャプチャに使うピンをしてします。この例ではタイマユニット0番のキャプチャピン0番をGPIO37で回路に接続することになります。MCPWM_UNIT_は0,1の2つMCPWM_CAP_は0,1,2の3つ合計6つ指定できます。
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, MCPWM_POS_EDGE, 0);
でキャプチャの設定と起動を行います。MCPWM_POS_EDGEは起動するタイミングの設定です。最後の0は指定した回数、信号が入力するまでキャプチャを行いません。0で1回の入力でキャプチャになります。

インプットキャプチャ割り込みルーチンも自作することができます。

ソースコード
#include "driver/mcpwm.h"

volatile unsigned int input_capture_data = 0; 
bool input_capture_call_back(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_channel, const cap_event_data_t *edata, void *user_data)
{
  static unsigned int old_number = 0;
  unsigned int number =  edata->cap_value;
  input_capture_data = number - old_number;
  old_number = number;
  return false;
}

void setup() {
  mcpwm_capture_config_t input_capture_setup;
  input_capture_setup.cap_edge = MCPWM_NEG_EDGE;
  input_capture_setup.cap_prescale = 1;
  input_capture_setup.capture_cb = input_capture_call_back;
  input_capture_setup.user_data = NULL;
  
  Serial.begin(115200);
  mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_0, 37);
  mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP0,&input_capture_setup ); 
}

void loop() {
  static unsigned int old_data = 0;
  if( old_data != input_capture_data ) {
    Serial.println(input_capture_data);
    old_data = input_capture_data;
  }
  delay(1);
}

参考サイト

pythonでシリアルプロッタ

2021-11-11 18:24:25 | Windows
pythonを使いシリアルプロッタを作成しました。

動作画面

ソースコード
# pythonによるシリアルプロッタ
# シリアルに対するデータの出力も可能

from tkinter import Tk,ttk
from tkinter import StringVar
from tkinter import Canvas
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import serial
import time
import threading

#シリアルポートの設定
def initSerial():
    # ポートの設定
    COM="COM57"      #通信ポート
    bitRate=115200   #通信速度
    ser = serial.Serial(COM, bitRate, timeout=0.1)
    return ser

#シリアルポートからデータの読み込み
datanum = 0
fullFlag = False
strage = []
def readSerial():
    global ser,datanum,fullFlag,strage
    data = ser.readline()       # シリアルからデータの読み込み
    if data != b"":             # 読み込んだデータが存在するか
        dataSTR = data.decode('utf-8')
        dataFLOAT = float(dataSTR)
        strage.append(dataFLOAT)
        if fullFlag :
            strage.pop(0)
        else :
            datanum = datanum + 1
            if datanum == 200 :
                fullFlag = True
    drawGraph()
    win.after(100,readSerial)
        
#グラフ描画領域作成
def initGraph(cvs):
    #グラフ用オブジェクト生成
    fig = plt.figure(figsize=(7, 5.5), dpi=100)   #Figure
    canvas = FigureCanvasTkAgg(fig, cvs)
    canvas.get_tk_widget().pack()
    plt.ion()
    return canvas,fig

#グラフの描画
def drawGraph():
    global canvas,strage
    # データをクリア
    plt.cla()
    plt.plot(strage,'blue')                   #2DLine
    canvas.draw()

#ウインドウの生成
def initWindow():
    #ウインドウを作成
    win = Tk()
    #ウインドウの設定
    win.title('graph')
    win.geometry('800x600')

    #ラベルを作成
    lbl = ttk.Label(win,text='送信')
    #ラベルの位置
    lbl.place(x=15,y=20)

    #ボタンを作成
    btn_0 = ttk.Button(text='0',width=3)
    btn_1 = ttk.Button(text='1',width=3)
    btn_2 = ttk.Button(text='2',width=3)
    btn_3 = ttk.Button(text='3',width=3)
    btn_4 = ttk.Button(text='4',width=3)
    btn_end = ttk.Button(text='exit',width=3)
    #ボタンの位置
    btn_0.place(x=15,y=40)
    btn_1.place(x=15,y=60)
    btn_2.place(x=15,y=80)
    btn_3.place(x=15,y=100)
    btn_4.place(x=15,y=120)
    btn_end.place(x=15,y=140)

    #キャンバスを作成
    cvs = Canvas(win,width=700,height=550)
    #キャンバスの位置
    cvs.place(x=75,y=25)
    #ボタンにイベントを設定
    btn_0.bind('<Button-1>',btnHandle_0)
    btn_1.bind('<Button-1>',btnHandle_1)
    btn_2.bind('<Button-1>',btnHandle_2)
    btn_3.bind('<Button-1>',btnHandle_3)
    btn_4.bind('<Button-1>',btnHandle_4)
    btn_end.bind('<Button-1>',btnHandle_end)
    #ウィンドウを閉じるときにイベントを設定
    win.protocol('WM_DELETE_WINDOW', _destroyWindow)
    return win,cvs

#ボタンが押された時の処理
def btnHandle_0(ev):
    ser.write(b"0\n")
    ser.flush()
#ボタンが押された時の処理
def btnHandle_1(ev):
    ser.write(b"1\n")
    ser.flush()
#ボタンが押された時の処理
def btnHandle_2(ev):
    ser.write(b"2\n")
    ser.flush()
#ボタンが押された時の処理
def btnHandle_3(ev):
    ser.write(b"3\n")
    ser.flush()
#ボタンが押された時の処理
def btnHandle_4(ev):
    ser.write(b"4\n")
    ser.flush()
#ボタンが押された時の処理
def btnHandle_end(ev):
    _destroyWindow()
# ウインドウが閉じられたとき
def _destroyWindow():
    ser.close()
    win.quit()
    win.destroy()

#メインプログラム
def main():
    global win,ser,canvas
    ser = initSerial()
    win,cvs = initWindow()
    canvas,fig = initGraph(cvs)
    win.after(100,readSerial)
    #ウインドウのメイン処理
    win.mainloop()

if __name__ == "__main__":
    main()

参考サイト

OneDriveクライアントのインストール

2020-11-25 21:24:39 | raspberry ...
Raspberry pi OS 64bit β版の動作しているRaspberry pi 4BにOneDriveクライアントをインストールしました。

必要なソフトウェアのインストール
$ sudo apt install libcurl4-openssl-dev libsqlite3-dev libxml2 pkg-config ldc libnotify-dev

ソースコードのダウンロードとビルド
$ git clone https://github.com/abraunegg/onedrive.git
$ cd onedrive
$ ./configure
$ make clean; make;
$ sudo make install

動作確認
$ onedrive --display-config
onedrive version                       = v2.4.6-16-g106e114
Config path                            = /home/pi/.config/onedrive
Config file found in config path       = false
Config option 'check_nosync'           = false
Config option 'sync_dir'               = /home/pi/OneDrive
Config option 'skip_dir'               = 
Config option 'skip_file'              = ~*|.~*|*.tmp
Config option 'skip_dotfiles'          = false
Config option 'skip_symlinks'          = false
Config option 'monitor_interval'       = 300
Config option 'min_notify_changes'     = 5
Config option 'log_dir'                = /var/log/onedrive/
Config option 'classify_as_big_delete' = 1000
Config option 'sync_root_files'        = false
Selective sync 'sync_list' configured  = false
Business Shared Folders configured     = false

認証
コマンドを実行するとURLが表示されるので、ブラウザでアクセスします。OneDriveの認証画面になり、認証させると空白のページが表示されます。この時のURLを入力します。
$ chmod 700  /home/pi/.config/onedrive/
$ onedrive --synchronize
Configuring Global Azure AD Endpoints
Authorize this app visiting:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx <-ブラウザでアクセス


Enter the response uri: https://login.microsoftonline.com/common/oauth2/nativeclient?code=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy <-ブラウザからコピー
Initializing the Synchronization Engine ...
Syncing changes from OneDrive ...
Creating local directory: ドキュメント
Creating local directory: 画像

認証に成功すると同期が始まります。同期するフォルダ・ファイルを制限したい場合は、[^C]で一度中断させます。

ファイル・フォルダの指定
ファイル"~/.config/onedrive/sync_list"をエディタで開き同期させたいフォルダ・ファイルを列挙します。
$ vi ~/.config/onedrive/sync_list
  ファイルの編集
$ onedrive --synchronize --resync  ファイルの同期
sync_listの例
Backup
Documents/latest_report.docx
Work/ProjectX
notes.txt

自動的に起動
自動的に起動するように設定します。
$ systemctl --user enable onedrive
$ systemctl --user start onedrive

参考

デフォルトユーザーpiの変更

2020-11-04 22:02:26 | raspberry ...
Raspberry pi OSの初期ユーザー名はpiとなっていますが、これを変更する方法です。

作業用ユーザーの作成
$ sudo adduser worku
ユーザ `worku' を追加しています...
新しいグループ `worku' (1001) を追加しています...
新しいユーザ `worku' (1001) をグループ `worku' として追加しています...
ホームディレクトリ `/home/worku' を作成しています...
`/etc/skel' からファイルをコピーしています...
新しいパスワード:
新しいパスワードを再入力してください:
passwd: パスワードは正しく更新されました
worku のユーザ情報を変更中
新しい値を入力してください。標準設定値を使うならリターンを押してください
        フルネーム []:
        部屋番号 []:
        職場電話番号 []:
        自宅電話番号 []:
        その他 []:
以上で正しいですか? [Y/n] Y
$ sudo gpasswd -a worku sudo
ユーザ worku をグループ sudo に追加
$

自動ログインの無効化

無効後に再起動させ作業用ユーザーworkuでログインします。

変更作業
workuでログイン後変更作業を行います。
$ who
worku    tty7         2020-11-04 14:14 (:0)
worku    pts/1        2020-11-04 14:15 (172.16.200.251)
$
他のユーザーがログインしていないことを確認してから、ユーザー"pi"を新しいIDの"newid"に変更します。
$ sudo usermod -l newid pi


あなたはシステム管理者から通常の講習を受けたはずです。
これは通常、以下の3点に要約されます:


    #1) 他人のプライバシーを尊重すること。
    #2) タイプする前に考えること。
    #3) 大いなる力には大いなる責任が伴うこと。


[sudo] worku のパスワード:
$ sudo usermod -d /home/newid -m newid
$ sudo groupmod -n newid pi
$ sudo passwd newid
この後設定ファイル内のユーザー"pi"を"newid"に変更します。
$ sudo vi /etc/lightdm/lightdm.conf
リストは抜粋 変更部部分赤字
#greeter-show-remote-login=true
#user-session=default
#allow-user-switching=true
#allow-guest=true
#guest-session=
#session-wrapper=lightdm-session
#greeter-wrapper=
#guest-wrapper=
display-setup-script=/usr/share/dispsetup.sh
#display-stopped-script=
#greeter-setup-script=
#session-setup-script=
#session-cleanup-script=
#autologin-guest=false
autologin-user=newid
#autologin-user-timeout=0
#autologin-in-background=false
#autologin-session=
#exit-on-failure=false


#
# XDMCP Server configuration
#

$ sudo vi /etc/systemd/system/autologin@.service
リストは抜粋 変更部分赤字
# getty.target didn't actually pull it in.
Before=getty.target
IgnoreOnIsolate=yes


# On systems without virtual consoles, don't start any getty. Note
# that serial gettys are covered by serial-getty@.service, not this
# unit.
ConditionPathExists=/dev/tty0


[Service]
# the VT is cleared by TTYVTDisallocate
ExecStart=-/sbin/agetty --autologin newid --noclear %I $TERM
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=%I
TTYPath=/dev/%I
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes


$ sudo vi /etc/sudoers.d/010_pi-nopasswd
変更部分赤字
newid ALL=(ALL) NOPASSWD: ALL

最後に作業用ユーザーを削除して再起動します。
sudo userdel -r worku

参考