tinkering好きの素人

ものづくりの記録

TouchDesigner で DMXをコントロールするまでの紆余曲折

まとめると、
安いopen DMX USBを使って、QLC+からはコントロールができた。
QLC+にOSCで送ろうと思って調べたが、うまく行かず原因は不明。送る際のチャンネル名が数字で終わってるとQLC側で受け取れないという記事を見つけたが、それを試す前にしびれを切らして注文したEnttec DMX USB Proが到着した。
Enttec使ったら
qiita.com
のサンプルが一瞬でうごきました!

ステージライト

安いDMXケーブル(TouchDesignerを使わないならこれで十分でした)中古で買ったEnttec DMX USB ProEnttecを使うために必要な変換ケーブル

pythonによるQRコード読み取りの文字化けを直した

webカメラやPC内蔵のカメラで日本語のQRを読み取る際、しょっちゅう文字化けしたので直した。
エンコードがshift-JISと思われたりutf-8と思われたりしてしまうのが原因だったよう。

from pyzbar.pyzbar import decode 
import cv2
   
capture = cv2.VideoCapture(0)#引数はPC内蔵なら0,webカメラなら1以降のことが多い
cv2.waitKey(10)
    
while(True):
        ret, frame = capture.read()
        gray_scale = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        image = edit_contrast(gray_scale, 5)
        codes = decode(image)
        cv2.waitKey(10)
        if len(codes) > 0:
            try:
                receive_text = codes[0].data.decode("utf-8").encode("shift-jis").decode("utf-8")
            except:
                receive_text = codes[0].data.decode("utf-8")

M5Stack ディスプレイへのpythonシリアル通信で日本語全角表示と半角表示を切り替えられるようにした

pythonスクリプトからシリアル通信で文字列を送信し、全角日本語文字と半角英語を表示できるようにしたかった。(ここではM5Stackと同時にArduinoにもつないでいます)

python -> M5Stack のシリアル通信にはpyserialを使用。(pip install pyserial)

import serial, time

def main():
    #  COMポートを開く
    print("Open Port")
    ser =serial.Serial("/dev/cu.#シリアルポート", 115200)#M5Stackへ
    ser2 = serial.Serial("/dev/cu.#シリアルポート",9600)#Arduinoへ
    while True:
        
        ser.write('ありがとうございます\0。漢字もだせるのかな\0?カタカナはどうでしょ\0\1'.encode('utf-8'))#出したい文字列をバイト列にしてutf-8エンコード
        ser2.write(b'1')
        time.sleep(4)#4秒ごとにきりかえ
        
        ser.write("'age': 22.0\n'gender': 'female'\n'smile': 0.0\n'hair': 'brown'\n\2".encode('utf-8'))
        ser2.write(b'0')
        
        time.sleep(4)

    print("Close Port")
    ser.close()
    
    
main()


Arduino側のコードは
qiita.com
のライブラリを使わせていただき、

#include <M5Stack.h>
#include <sdfonts.h>

#define SD_PN 4

// フォントデータの表示
// buf(in) : フォント格納アドレス
// ビットパターン表示
// d: 8ビットパターンデータ
void fontDisp(uint16_t x, uint16_t y, uint8_t* buf) {
  uint32_t txt_color = TFT_WHITE;
  uint32_t bg_color = TFT_BLACK;

  uint8_t bn = SDfonts.getRowLength();               // 1行当たりのバイト数取得
//  Serial.print(SDfonts.getWidth(), DEC);            // フォントの幅の取得
//  Serial.print("x");
//  Serial.print(SDfonts.getHeight(), DEC);           // フォントの高さの取得
//  Serial.print(" ");
//  Serial.println((uint16_t)SDfonts.getCode(), HEX); // 直前し処理したフォントのUTF16コード表示

  for (uint8_t i = 0; i < SDfonts.getLength(); i += bn ) {
    for (uint8_t j = 0; j < bn; j++) {
      for (uint8_t k = 0; k < 8; k++) {
        if (buf[i + j] & 0x80 >> k) {
          M5.Lcd.drawPixel(x + 8 * j + k , y + i / bn, txt_color);
        } else {
          M5.Lcd.drawPixel(x + 8 * j + k , y + i / bn, bg_color);
        }
      }
    }
  }
}


// 指定した文字列を指定したサイズで表示する
// pUTF8(in) UTF8文字列
// sz(in)    フォントサイズ(8,10,12,14,16,20,24)
void fontDump(uint16_t x, uint16_t y, char* pUTF8, uint8_t sz) {
  uint8_t buf[MAXFONTLEN]; // フォントデータ格納アドレス(最大24x24/8 = 72バイト)
  SDfonts.open();                                   // フォントのオープン
  SDfonts.setFontSize(sz);                          // フォントサイズの設定
  uint16_t mojisu = 0;
  while ( pUTF8 = SDfonts.getFontData(buf, pUTF8) ) { // フォントの取得
    fontDisp(x + mojisu * sz, y, buf);                 // フォントパターンの表示
    ++mojisu;
  }

  SDfonts.close();                                  // フォントのクローズ
}

char printchar;


void setup() {
  Serial.begin(115200);

  M5.begin();

  M5.Lcd.setBrightness(100);
  M5.Lcd.fillScreen(TFT_BLACK);

  SDfonts.init(SD_PN);
}

char buff[255];
char kara[255];
int counter = 0;
int LED = 13;
int gyo = 0;
int flag = 0;

void loop() {
  // put your main code here, to run repeatedly:
  while(Serial.available()){
    if(gyo == 0){
      M5.Lcd.fillScreen(TFT_BLACK);
    }
    char inChar = char(Serial.read());
    buff[counter] = inChar;
    counter++;  
    if (inChar == '\0' && flag == 1){
      //全角
      fontDump(10, 10+gyo*30, buff, 24);
      gyo++;
      Serial.println(buff);
      counter = 0;
      digitalWrite(LED, HIGH);
      buff = '';
      memset(buff,'\0',sizeof(buff));
      
    }else if (inChar == '\n' && flag == 0){
      //半角
      M5.Lcd.setTextSize(2);
      M5.Lcd.drawString(buff,10,10+gyo*30);
      gyo++;
      Serial.println(buff);
      counter = 0;
      memset(buff,'\0',sizeof(buff));
          }else if(inChar == '\1'){
        flag=0;
        counter = 0;
        gyo = 0;
      }else if(inChar == '\2'){
        flag=1;
        counter = 0;
        gyo = 0;
      }
  }
}

f:id:minoritytech:20200818113203p:plain
f:id:minoritytech:20200818113550p:plain

ラズパイ起動スクリプト ~opencv-pythonでウィンドウを開きたい場合

Raspberry Pi 3 の起動スクリプト作成でつまずいたのでメモ。
local.rcからの実行やsystemd,crontab での実行を試しましたがうまくいかず。autostartで行ってやっとうまくいきました。
qiita.com
こちらの方が詳しく書いていらっしゃった、ちゃんと読むべきでした。

openframeworks for ios でエラーが出続けた

デバッグに半日かかって辛かったので数年ぶりに記事かきます。

環境は
mac OS Catalina 10.15.4
xcode 11.4.1
です。

openframeworks for iosは最新の0.11を使おうとしてたのですがエラー出すぎて0.10.1を使いました。

やろうとしていたことは前に作ったiphone でfacetracking するアプリがなぜかビルドエラーが出るようになってしまったので、それを解決すること。
このアプリの詳細については書きませんが、ofxFaceTrackerとofxOpenCVとofxCVを使っていました。

記憶が曖昧ですが、たしか最初に出たエラーは
duplicate symbols for architecture arm64
でした。
結論から言うと、これは
libofxiOS_iphoneos_Debug~
みたいなファイルをframework(写真の場所)から削除したら直った。(ここまでが長かった…)

f:id:minoritytech:20200523230341p:plain


他に起こり続けたエラーは、
opencv/cv.h not found
これはopencvのバージョンがおかしくなってるせいだったみたいだったのですが、とにかくopencvのヘッダファイルたちを下の写真のように
/of_v0.11.0_ios_release/addons/ofxOpenCv/libs/opencv/include/opencv
に貼り付けました。(ここに至るまでbuild settings のheader search paths をいじり続けていましたが、私の場合おそらくこれで十分だったはず)

f:id:minoritytech:20200523230628p:plain

rtcのアラーム機能を使う

ArduinoでHiletgoのRTCモジュールを使って、アラーム機能で1分に1回LEDを点ける。使ったのはこちら


配線は皆さん書かれているように、Arduino->rtcで
A4->SDA,A5->SCL,5V->VCC,GND->GND

ライブラリは以下を使えばI2Cしなくていい
zipダウンロードしてArduinoのスケッチ>ライブラリをインクルードからzipを指定を選択してインクルード。参照できないライブラリはコード横にURLがあるので適宜追加。

github.com


上記のgitにあるalarm_ex1をほんのすこしだけいじった。毎分05秒でArduinoのビルトインLEDが1秒光る。
このコードなら、一旦電源をとってしまってもCR2032をつけておけばもう一度つけたとき時計がずれることはなかった。念の為アマゾンのレビューにもあるように201の抵抗をはずした。

// Arduino DS3232RTC Library
// https://github.com/JChristensen/DS3232RTC
// Copyright (C) 2018 by Jack Christensen and licensed under
// GNU GPL v3.0, https://www.gnu.org/licenses/gpl.html
//
// DS3231/DS3232 Alarm Example Sketch #1
//
// Set Alarm 1 to occur once a minute at 5 seconds after the minute.
// Detect the alarm by polling the RTC alarm flag.
//
// Hardware:
// Arduino Uno, DS3231 RTC.
// Connect RTC SDA to Arduino pin A4.
// Connect RTC SCL to Arduino pin A5.
//
// Jack Christensen 16Sep2017

#include <DS3232RTC.h>      // https://github.com/JChristensen/DS3232RTC
#include <Streaming.h>      // http://arduiniana.org/libraries/streaming/

void setup()
{
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);

  // initialize the alarms to known values, clear the alarm flags, clear the alarm interrupt flags
  RTC.setAlarm(ALM1_MATCH_DATE, 0, 0, 0, 1);
  RTC.setAlarm(ALM2_MATCH_DATE, 0, 0, 0, 1);
  RTC.alarm(ALARM_1);
  RTC.alarm(ALARM_2);
  RTC.alarmInterrupt(ALARM_1, false);
  RTC.alarmInterrupt(ALARM_2, false);
  RTC.squareWave(SQWAVE_NONE);

  // set the RTC time and date to the compile time
  //    RTC.set(compileTime());

  // set Alarm 1 to occur at 5 seconds after every minute
  RTC.setAlarm(ALM1_MATCH_SECONDS, 5, 0, 0, 1);
  // clear the alarm flag
  RTC.alarm(ALARM_1);

  Serial << millis() << " Start ";
  printDateTime(RTC.get());
  Serial << endl;
}

void loop()
{
  if ( RTC.alarm(ALARM_1) )    // check alarm flag, clear it if set
  {
    Serial << millis() << " ALARM_1 ";
    printDateTime(RTC.get());
    Serial << endl;
    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(1000);                       // wait for a second
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
    delay(1000);

  }
  if ( RTC.alarm(ALARM_2) )    // check alarm flag, clear it if set
  {
    Serial << millis() << " ALARM_2 ";
    printDateTime(RTC.get());
    Serial << endl;
  }
  delay(100);                  // no need to bombard the RTC continuously
}

void printDateTime(time_t t)
{
  Serial << ((day(t) < 10) ? "0" : "") << _DEC(day(t));
  Serial << monthShortStr(month(t)) << _DEC(year(t)) << ' ';
  Serial << ((hour(t) < 10) ? "0" : "") << _DEC(hour(t)) << ':';
  Serial << ((minute(t) < 10) ? "0" : "") << _DEC(minute(t)) << ':';
  Serial << ((second(t) < 10) ? "0" : "") << _DEC(second(t));
}

// function to return the compile date and time as a time_t value
time_t compileTime()
{
  const time_t FUDGE(10);    //fudge factor to allow for upload time, etc. (seconds, YMMV)
  const char *compDate = __DATE__, *compTime = __TIME__, *months = "JanFebMarAprMayJunJulAugSepOctNovDec";
  char compMon[3], *m;

  strncpy(compMon, compDate, 3);
  compMon[3] = '\0';
  m = strstr(months, compMon);

  tmElements_t tm;
  tm.Month = ((m - months) / 3 + 1);
  tm.Day = atoi(compDate + 4);
  tm.Year = atoi(compDate + 7) - 1970;
  tm.Hour = atoi(compTime);
  tm.Minute = atoi(compTime + 3);
  tm.Second = atoi(compTime + 6);

  time_t t = makeTime(tm);
  return t + FUDGE;        //add fudge factor to allow for compile time
}