SSブログ

SPRESENSE用のXMODEMライブラリを作ってみた! [SPRESENSE]

SPRESENSE用のXMODEMライブラリを作ってみました! GitHub のリポジトリを作りましたので、興味ある方はご覧ください。

s_github.png YoshinoTaro/CecXModem128

Contribute to YoshinoTaro/CecXModem128 development by creating an account on GitHub.
github.com



SPRESENSEはUSBもあるしSDカードもあるし必要なの?と思う人は多いと思いますが、これがあるとすごく便利なんです。例えば、起動時に最新の学習済データを転送して、定期的にログに落とすということをメインボードのUSB-UARTだけで行なえます。

ラズパイなどと組み合わせることで、スケッチのパラメータを自動的に更新やログの吸い上げなど処理の自動化が行えます。それよりも何よりも内蔵フラッシュに直接アクセスできるので大きな拡張ボードは不要になります。(∩´∀`)∩ワーイ

使い方もすごく簡単です。例えば、Spresenseの内蔵フラッシュからPCにデータを送るのは次のスケッチで実現できます。

#include <CrcXModem128.h>
#include <Flash.h>

CrcXModem128 xmodem;

void setup() {
  Serial.begin(115200);
  Flash.begin();
  xmodem.begin(&Serial);

  Serial.println("ready to receive");

  /* Spresense -> PC */
  File sndFile = Flash.open("sndtxt.txt");
  Serial.println("ready to send");

  if (sndFile) {
    Serial.println("XModem ready!");
    xmodem.sendFile(sndFile);
  } else {
    Serial.println("sndtxt.txt doesnot exists");
  }
  sndFile.close();
}

void loop() { }    




一方、PCからSpresenseの内蔵フラッシュにデータを送るのは次のスケッチで実現できます。

#include <CrcXModem128.h>
#include <Flash.h>

CrcXModem128 xmodem;

void setup() {
  Serial.begin(115200);
  Flash.begin();
  xmodem.begin(&Serial);
  
  Serial.println("ready to receive");

  /* PC -> Spresense */
  File rcvFile = Flash.open("rcvtxt.txt", FILE_WRITE);

  if (rcvFile) {  
    Serial.println("XModem ready!");
    xmodem.recvFile(rcvFile);
  } else {
    Serial.println("rcvtxt.txt cannot create");
  }
  rcvFile.close();
}

void loop() { }



Spresenseでしか動作確認していませんが、ライブラリそのものは特別なAPIを使っていないので、他の Arduino でも動くかもしれません。もし、チャレンジされた方がいたら一報いただけると助かります。
(^^)/~





SONY SPRESENSE メインボード CXD5602PWBMAIN1

SONY SPRESENSE メインボード CXD5602PWBMAIN1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware



SONY SPRESENSE カメラモジュール CXD5602PWBCAM1

SONY SPRESENSE カメラモジュール CXD5602PWBCAM1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware



SONY SPRESENSE LTE拡張ボード CXD5602PWBLM1J

SONY SPRESENSE LTE拡張ボード CXD5602PWBLM1J

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア:




タグ:XMODEM Spresense
nice!(26)  コメント(0) 
共通テーマ:趣味・カルチャー

【朗報】SPRESENSE の CircuitPython が "GPS"をサポート! [SPRESENSE]

前回、SPRESENSEでCircuitPythonを試してみたのはだいたい3ヶ月くらい前。


python-spresense.png
■ SPRESENSE で "CircuitPython" を動かしてみた!
https://makers-with-myson.blog.ss-blog.jp/2020-05-08
■ SPRESENSE用”CircuitPython”のビルド環境を作ろう!(目次編
https://makers-with-myson.blog.ss-blog.jp/2020-05-10


前回はまだ完成度が低く発展途上だったのでアップデートはあるだろうなと思い、時々 CircuitPythonの GitHub の IssueList をチェックしているのですが、先日こんな "Issue" を発見!


IssueList.png


早速、最新のツリーをCloneしてビルドしてみました。ビルドの仕方はこちらを参照してください。


2020-05-09.png


GNSSとSDカードの確認をするためにサンプルコードを組んで試してみました。(サンプルコードは shared-bindings のソースコードの中にコメントとして記述されています)


■ GNSSのサンプルコード
import gnss
import time

print("start")

nav = gnss.GNSS([gnss.SatelliteSystem.GPS, gnss.SatelliteSystem.GLONASS])
last_print = time.monotonic()

while True:
    nav.update()
    current = time.monotonic()
    if current - last_print >= 1.0:
        last_print = current
        if nav.fix is gnss.PositionFix.INVALID:
            print("Waiting for fix...")
            continue
        print("Latitude: {0:.6f} degrees".format(nav.latitude))
        print("Longitude: {0:.6f} degrees".format(nav.longitude))




こちらのコンソール出力がこれ。おぉ、部屋の中からですが位置を補足してます。なかなか偉いですねSPRESENSE。位置情報が得られるようになると色々とアプリケーションを作る気が湧いてきます。


logout.png


USBケーブルは拡張ボード側のUSBポートに接続してください。Arduino IDE と異なり、シリアル出力も拡張ボードのUSBポートから出力されます。間違ってメインボードに挿して、シリアルが出ないと慌てないようにしてください。


次はSDカードの確認です。CircuitPython 標準の "sdcardio" と実装が別になってるのはなんでじゃろ?と思ったら "sdcardio" のほうはSPIなんですね。SPRESENSEのSDカードは高速のSDIOインターフェースなので別の実装が必要だったみたいです。


■ SDIOのサンプルコード
import sdioio
import storage
import os


print("start")

sd = sdioio.SDCard(clock=board.SDIO_CLOCK, command=board.SDIO_COMMAND, data=board.SDIO_DATA, frequency=2500000)

vfs = storage.VfsFat(sd)
storage.mount(vfs, '/sd')
fp = open("/sd/tmp.txt", "a")
fp.write("hello world!")
fp.close()



code.py を保存するとすぐに動き出し、SDカード上に”Hello World!”が書き込まれた "tmp.txt" が出来上がりました。やった!

これで位置情報をSDカードに記録するトラッカーが出来上がりますね。拡張ボードは大きいので、スイッチサイエンスで売っている小さな拡張ボードだと小さなGPS トラッカーが出来上がりそうです。


Kaspi.jpg
https://www.switch-science.com/catalog/5374/


これからのアップデートをさらに期待したいところですね!
( ^ω^ )/~





SONY SPRESENSE メインボード CXD5602PWBMAIN1

SONY SPRESENSE メインボード CXD5602PWBMAIN1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware



SONY SPRESENSE 拡張ボード CXD5602PWBEXT1

SONY SPRESENSE 拡張ボード CXD5602PWBEXT1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware



Getting Started with Adafruit Circuit Playground Express: The Multipurpose Learning and Development Board with Built-In LEDs, Sensors, and Accelerometer

Getting Started with Adafruit Circuit Playground Express: The Multipurpose Learning and Development Board with Built-In LEDs, Sensors, and Accelerometer

  • 作者: Barela, Anne
  • 出版社/メーカー: Make Community, LLC
  • 発売日: 2018/09/18
  • メディア: ペーパーバック




nice!(24)  コメント(0) 
共通テーマ:趣味・カルチャー

SPRESENSEスペクトログラムをアップデート!! [SPRESENSE]

SPRESENSEスペクトログラムをアップデートしました。今回は微妙な変化がわかるようにグレースケールで表示してみました。あとピーク周波数を出すようにしています。


sDSC_0008.jpg


実際の動きはこちらをご覧ください。





スケッチはこちら。解析はメインコアで、表示はサブスコアで行っています。MP.Mutex でサブスコアにデータを送るタイミングをとっています。


■ メインコアのスケッチ
#ifdef SUBCORE
#error "Core selection is wrong!!"
#endif

#include 
#include 
#include 
#define SUBCORE1 1

MPMutex mutex(MP_MUTEX_ID0);
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

#include 
#include 
#include 
#define FFT_LEN  256
#define CHANNEL_NUM 1

// #define LPF_ENABLE
#define LPF_CUTOFF 3000
#define LPF_QVALUE 0.70710678

// #define SMA_ENABLE
#define SMA_WINDOW 8

FFTClass FFT;
IIRClass LPF;

AudioClass *theAudio = AudioClass::getInstance();
static const int32_t buffer_sample = FFT_LEN;
static const int32_t buffer_size = buffer_sample * sizeof(int16_t);
static const uint32_t sampling_rate = AS_SAMPLINGRATE_48000;
static char buff[buffer_size];

static float pDst[FFT_LEN];
static float pOut[FFT_LEN];

#ifdef SMA_ENABLE
static float pSMA[SMA_WINDOW][FFT_LEN];

void applySMA(float sma[SMA_WINDOW][FFT_LEN], float dst[FFT_LEN]) 
{
  int i, j;
  static int g_counter = 0;
  if (g_counter == SMA_WINDOW) g_counter = 0;
  for (i = 0; i < FFT_LEN; ++i) {
    sma[g_counter][i] = dst[i];
    float sum = 0;
    for (j = 0; j < SMA_WINDOW; ++j) {
      sum += sma[j][i];
    }
    dst[i] = sum / SMA_WINDOW;
  }
  ++g_counter;
}
#endif

static void audioReadFrames() {
  int err, ret;
  uint32_t read_size;
  static bool bInit = true;
  static q15_t pLPFSig[buffer_size];
  static const unsigned long wait_usec =  (double)(buffer_sample)/sampling_rate*1000000;
  Serial.println(wait_usec);
  
  while(1) {
    err = theAudio->readFrames(buff, buffer_size, &read_size);
    if (err != AUDIOLIB_ECODE_OK && err != AUDIOLIB_ECODE_INSUFFICIENT_BUFFER_AREA) {
      Serial.println("Error err = " + String(err));
      theAudio->stopRecorder();
      exit(1);
    }

    if (read_size < buffer_size) {
      usleep(wait_usec);
      continue;
    }
    
#ifdef LPF_ENABLE
    // 3kHz low pass filter
    LPF.put((q15_t*)buff, buffer_sample);
    LPF.get(pLPFSig, 0);
    FFT.put(pLPFSig, buffer_sample);
#else
    FFT.put((q15_t*)buff, buffer_sample);
#endif

    if (bInit) { bInit = false; continue; }

    // Using mutex to protect pDst array
    if (pthread_mutex_lock(&m) != 0) Serial.println("Mutex Lock Error");
    FFT.get(pDst, 0);
#ifdef SMA_ENABLE
    applySMA(pSMA, pDst);
#endif
    if (pthread_mutex_unlock(&m) != 0) Serial.println("Mutex UnLock Error");

  }
}

void setup() {
  int8_t sndid; 
  Serial.begin(115200);
  
  MP.begin(SUBCORE1);
  sndid = 101;
  MP.Send(sndid, FFT_LEN, SUBCORE1);
  MP.RecvTimeout(MP_RECV_POLLING); 


#ifdef LPF_ENABLE
  LPF.begin(TYPE_LPF, CHANNEL_NUM, LPF_CUTOFF, LPF_QVALUE);
#endif
  FFT.begin(WindowRectangle, CHANNEL_NUM, (FFT_LEN/2));
  
  theAudio->begin();
  // theAudio->setRecorderMode(AS_SETRECDR_STS_INPUTDEVICE_MIC, 200);
  theAudio->setRecorderMode(AS_SETRECDR_STS_INPUTDEVICE_MIC);
  int err = theAudio->initRecorder(AS_CODECTYPE_PCM ,"/mnt/sd0/BIN" 
                           ,AS_SAMPLINGRATE_48000 ,AS_CHANNEL_MONO);                             
  if (err != AUDIOLIB_ECODE_OK) {
    Serial.println("Recorder initialize error");
    while(1);
  }

  theAudio->startRecorder(); 
  Serial.println("Start Recording");
  task_create("audio recording", 120, 1024, audioReadFrames, NULL);
  sleep(1);
}

void loop() {
  int err, ret;
  int8_t sndid;

  // Using mutex to protect pDst array
  if (pthread_mutex_lock(&m) != 0) Serial.println("Mutex Lock Error");
  memcpy(pOut, pDst, buffer_size);
  if (pthread_mutex_unlock(&m) != 0) Serial.println("Mutex UnLock Error");

  // Using MPMutex to check the availablity of SubCore
  if (mutex.Trylock() != 0) { usleep(20000); return; }
  sndid = 100;
  err = MP.Send(sndid, &pOut, SUBCORE1);
  if (err < 0) Serial.println("MP Send error\n");
  mutex.Unlock();  
}



■ サブコアのスケッチ

#if (SUBCORE != 1)
#error "Core selection is wrong!!"
#endif

#define ARM_MATH_CM4
#define __FPU_PRESENT 1U
#include 

#include 
#include 
#include 

#include 
#include 

#define CS  10
#define DC   9   
#define RST  8
Adafruit_ILI9341 tft = Adafruit_ILI9341(CS, DC, RST);

MPMutex mutex(MP_MUTEX_ID0);

#define SPECTRO_WIDTH  (128)
#define SPECTRO_HEIGHT (320)
static uint16_t frameBuffer[SPECTRO_HEIGHT][SPECTRO_WIDTH];

#define DEBUG
// #define PSEUDO_COLOR

static uint16_t fft_len = 0;

float get_peak_frequency(float *pData, int fftLen)
{
  float g_fs = 48000.0f;
  uint32_t index;
  float maxValue;
  float delta;
  float peakFs;

  arm_max_f32(pData, fftLen / 2, &maxValue, &index);

  delta = 0.5 * (pData[index - 1] - pData[index + 1])
    / (pData[index - 1] + pData[index + 1] - (2.0f * pData[index]));
  peakFs = (index + delta) * g_fs / (fftLen - 1);

  return peakFs;
}

void setup() {
  int8_t recvid;
  uint32_t msg; 
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);
  tft.setCursor(35, 210);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(2);
  tft.println("FFT Spectrogram Viewer");
  tft.setTextColor(ILI9341_BLUE);
  tft.setTextSize(2);
  tft.setCursor(35, 35);
  tft.print("Peak FS:");
  tft.setCursor(210, 35);
  tft.print("Hz");
  tft.setRotation(2);
  memset(frameBuffer, 255, SPECTRO_WIDTH*SPECTRO_HEIGHT*sizeof(uint16_t));

  MP.begin();

  MP.Recv(&recvid, &msg);
  fft_len = msg;
  MPLog("%d FFT_LEN %d\n", recvid, fft_len);
  MP.RecvTimeout(MP_RECV_POLLING); 
}

void loop() {

  // based on CXD5247 technical manual
  static const float spr_signal_noise_ratio = 90.0; // SNR 
 
  static uint16_t colormap[] = {
    ILI9341_MAGENTA,
    ILI9341_BLUE,
    ILI9341_CYAN,
    ILI9341_GREEN,
    ILI9341_YELLOW,
    ILI9341_ORANGE,
    ILI9341_RED,
    ILI9341_WHITE
  };
  
  int8_t msgid;
  float *data;
  int ret, i, j;
  float f_max, f_min;
  
  ret = MP.Recv(&msgid, &data);
  if (ret < 0)  return;

  // Using MPMutex to notify MainCore that SubCore is in busy
  do { ret = mutex.Trylock(); } while (ret != 0);

#ifdef DEBUG
  f_max = 0.0;
  f_min = 1000.0;
  for (i = 0; i < fft_len/2; ++i) {
    float val = abs(data[i]);
    // MPLog("[%d] %f\n", i, val);
    if (val == 0.0) continue;
    if (val > f_max) f_max = val;
    if (val < f_min) f_min = val;
  }
  MPLog("RANGE: %3.6f - %+3.6f\n", f_max, f_min);
#endif
  
  f_max = -1000.0;
  f_min =  1000.0;
  for (i = 0; i < fft_len/2; ++i) {
    // if (!isnan(data[i]) && data[i] > 0.0) { 
    if (data[i] != 0.0) { 
      data[i] = 20.*log10(abs(data[i])) + spr_signal_noise_ratio;
      if (data[i] > f_max) f_max = data[i];
      if (data[i] < f_min) f_min = data[i];
    } 
  }
  MPLog("Range: %3.6f - %3.6f\n\n", f_max, f_min);

  for (i = 1; i < SPECTRO_HEIGHT; ++i) {
    for (j = 0; j < SPECTRO_WIDTH; ++j) {
      frameBuffer[i-1][j] = frameBuffer[i][j];
    }
  }

  // display range:0:0Hz - 200:9.375kHz
  for (i = 0; i < SPECTRO_WIDTH; ++i) {
    float f_val = data[i];
#ifdef PSEUDO_COLOR
    static const float magnify = 1;
    uint8_t index = magnify*f_val/32; 
    frameBuffer[SPECTRO_HEIGHT-1][i] = colormap[index];
#else
    uint16_t val_6, val_5;
    val_6 = (uint32_t)(f_val) * 64 / 256;
    val_5 = (uint32_t)(f_val) * 32 / 256;
    uint16_t val = val_5 << 11 | val_6 << 5 | val_5; 
    frameBuffer[SPECTRO_HEIGHT-1][i] = val;
#endif
  }
  
  tft.drawRGBBitmap(40, 0, (uint16_t*)frameBuffer, SPECTRO_WIDTH, SPECTRO_HEIGHT);

  uint16_t peak_fs = get_peak_frequency(data, fft_len);

  static int g_counter = 0;

  if (g_counter % 10 == 0) {
    char num_buf[8];
    tft.setRotation(3);
    tft.setTextColor(ILI9341_BLUE);
    tft.setTextSize(2);
    tft.setCursor(140, 35);
    tft.fillRect(140, 35, 60, 15, ILI9341_BLACK);
    sprintf(num_buf, "%05d", peak_fs);
    tft.print(num_buf);
    tft.setRotation(2);
    g_counter = 1;
  } else {
    ++g_counter;
  }

  mutex.Unlock();
}



次はどうやって学習データを記録していくか考えたいと思います。
(。-`ω´-)ンー





SONY SPRESENSE メインボード CXD5602PWBMAIN1

SONY SPRESENSE メインボード CXD5602PWBMAIN1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware



SONY SPRESENSE 拡張ボード CXD5602PWBEXT1

SONY SPRESENSE 拡張ボード CXD5602PWBEXT1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware



Voice Assistant Using SONY Spresense (English Edition)

Voice Assistant Using SONY Spresense (English Edition)

  • 作者: Perez, Guillermo
  • 出版社/メーカー:
  • 発売日: 2019/03/11
  • メディア: Kindle版




nice!(25)  コメント(0) 
共通テーマ:趣味・カルチャー

SPRESENSE でスペクトログラム出力をしてみた [SPRESENSE]

前回、Python でスペクトログラム出力を検討しましたが、今回は SPRESENSE でスペクトログラム出力ができるようにしました!


DSC_1018.JPG


画質は悪いですが、動作している様子をTwitterにあげました。そのうちYoutubeにもあげたいと思います!





このプログラムは、最近新しく追加されたSPRESENSEの Signal Processing ライブラリを使いました。もう少しリファインが必要ですがコードを晒します。表示を少しでも早くするために、信号処理と表示をサブスコアを使って並行処理にしています。

こんな芸当ができるのも、SPRESENSEならではですね。(^^)/~


■ メインコアのスケッチ
#ifdef SUBCORE
#error "Core selection is wrong!!"
#endif

#include <MP.h>
#include <MPMutex.h>
#include <pthread.h>
#define SUBCORE1 1

MPMutex mutex(MP_MUTEX_ID0);
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;

#include <Audio.h>
#include <FFT.h>
#include <IIR.h>
#define FFT_LEN 1024
#define SMA_WINDOW 8
#define CHANNEL_NUM 1
#define LPF_CUTOFF 3000
#define LPF_QVALUE 0.70710678

FFTClass<CHANNEL_NUM, FFT_LEN> FFT;
IIRClass LPF;

AudioClass *theAudio = AudioClass::getInstance();
static const int32_t buffer_sample = FFT_LEN;
static const int32_t buffer_size = buffer_sample * sizeof(int16_t);
static char buff[buffer_size];
uint32_t read_size;

static float pDst[FFT_LEN];
static float pOut[FFT_LEN/2];
static float pSMA[SMA_WINDOW][FFT_LEN];


void applySMA(float sma[SMA_WINDOW][FFT_LEN], float dst[FFT_LEN]) 
{
  int i, j;
  static int g_counter = 0;
  if (g_counter == SMA_WINDOW) g_counter = 0;
  for (i = 0; i < FFT_LEN; ++i) {
    sma[g_counter][i] = dst[i];
    float sum = 0;
    for (j = 0; j < SMA_WINDOW; ++j) {
      sum += sma[j][i];
    }
    dst[i] = sum / SMA_WINDOW;
  }
  ++g_counter;
}

static void audioReadFrames() {
  int err, ret;
  static int g_counter = 0;
  static q15_t pLPFSig[FFT_LEN];
  
  while(1) {
    err = theAudio->readFrames(buff, buffer_size, &read_size);
    if (err != AUDIOLIB_ECODE_OK && err != AUDIOLIB_ECODE_INSUFFICIENT_BUFFER_AREA) {
      Serial.println("Error err = " + String(err));
      theAudio->stopRecorder();
      exit(1);
    }

    if (read_size < buffer_size) {
      usleep(10000);
      continue;
    }

    // 3kHz low pass filter
    LPF.put((q15_t*)buff, FFT_LEN);
    LPF.get(pLPFSig, 0);

    // FFT
    FFT.put(pLPFSig, FFT_LEN);
    // Using mutex to protect pDst array
    if (pthread_mutex_lock(&m) != 0) Serial.println("Mutex Lock Error");
    FFT.get(pDst, 0);
    applySMA(pSMA, pDst);
    if (pthread_mutex_unlock(&m) != 0) Serial.println("Mutex UnLock Error");
    
    usleep(8000);
  }
}

void setup() { 
  Serial.begin(115200);
  MP.begin(SUBCORE1);
  MP.RecvTimeout(MP_RECV_POLLING); 

  LPF.begin(TYPE_LPF, CHANNEL_NUM, LPF_CUTOFF, LPF_QVALUE);
  FFT.begin(WindoHanning, 1, (FFT_LEN/4));
  
  theAudio->begin();
  theAudio->setRecorderMode(AS_SETRECDR_STS_INPUTDEVICE_MIC, 200);
  int err = theAudio->initRecorder(AS_CODECTYPE_PCM ,"/mnt/sd0/BIN" 
                           ,AS_SAMPLINGRATE_48000 ,AS_CHANNEL_MONO);                             
  if (err != AUDIOLIB_ECODE_OK) {
    Serial.println("Recorder initialize error");
    while(1);
  }

  theAudio->startRecorder(); 
  Serial.println("Start Recording");
  task_create("audio recording", 120, 1024, audioReadFrames, NULL);
  sleep(1);
}

void loop() {
  int err, ret;
  int8_t sndid;

  // Using mutex to protect pDst array
  if (pthread_mutex_lock(&m) != 0) Serial.println("Mutex Lock Error");
  memcpy(pOut, pDst, buffer_size /2);
  if (pthread_mutex_unlock(&m) != 0) Serial.println("Mutex UnLock Error");

  // Using MPMutex to check the availablity of SubCore
  if (mutex.Trylock() != 0) { usleep(20000); return; }
  sndid = 100;
  err = MP.Send(sndid, &pOut, SUBCORE1);
  if (err < 0) Serial.println("MP Send error\n");
  mutex.Unlock();  
}



■ サブスコアのスケッチ
#if (SUBCORE != 1)
#error "Core selection is wrong!!"
#endif

#include <MP.h>
#include <MPMutex.h>

#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

#define CS  10
#define DC   9   
#define RST  8
Adafruit_ILI9341 tft = Adafruit_ILI9341(CS, DC, RST);

MPMutex mutex(MP_MUTEX_ID0);

#define FFT_LEN 1024
#define SPECTRO_WIDTH (FFT_LEN/8)
#define SPECTRO_HEIGHT (320)
static uint16_t frameBuffer[SPECTRO_HEIGHT][SPECTRO_WIDTH];

void setup() {
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);
  tft.setCursor(35, 210);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(2);
  tft.println("FFT Spectrogram Viewer");
  tft.setRotation(2);
  memset(frameBuffer, 255, SPECTRO_WIDTH*SPECTRO_HEIGHT*sizeof(uint16_t));

  MP.begin();
  MP.RecvTimeout(MP_RECV_POLLING); 
}

void loop() {

  // based on CXD5247 technical manual
  static const float spr_signal_noise_ratio = 90.0; // SNR 
 
  static uint16_t colormap[] = {
    ILI9341_MAGENTA,
    ILI9341_BLUE,
    ILI9341_CYAN,
    ILI9341_GREEN,
    ILI9341_YELLOW,
    ILI9341_ORANGE,
    ILI9341_RED,
  };
  
  int8_t msgid;
  float *data;
  int ret, i, j;
  
  ret = MP.Recv(&msgid, &data);
  if (ret < 0)  return;

  // Using MPMutex to notify MainCore that SubCore is in busy
  do { ret = mutex.Trylock(); } while (ret != 0);
  for (i = 0; i < FFT_LEN/2; ++i) {
    if (!isnan(data[i]) && data[i] > 0.0) { 
      data[i] = 20.*log10(data[i]) + spr_signal_noise_ratio;
    } else {
      data[i] = 0.0; // under the noise level?
    }
  }

  for (i = 1; i < SPECTRO_HEIGHT; ++i) {
    for (j = 0; j < SPECTRO_WIDTH; ++j) {
      frameBuffer[i-1][j] = frameBuffer[i][j];
    }
  }

  // display range:0:0Hz - FFT_LEN/8(128):6kHz
  static const float magnify = 1.0; // To magnify the signal
  for (i = 0; i < SPECTRO_WIDTH; ++i) { 
    uint8_t index = magnify*data[i]/32; 
    frameBuffer[SPECTRO_HEIGHT-1][i] = colormap[index];
  }
  
  tft.drawRGBBitmap(40, 0, (uint16_t*)frameBuffer, SPECTRO_WIDTH, SPECTRO_HEIGHT);
  mutex.Unlock();
}






サウンドプログラミング入門――音響合成の基本とC言語による実装 (Software Design plus)

サウンドプログラミング入門――音響合成の基本とC言語による実装 (Software Design plus)

  • 作者: 青木 直史
  • 出版社/メーカー: 技術評論社
  • 発売日: 2013/02/01
  • メディア: 単行本(ソフトカバー)



SONY SPRESENSE メインボード CXD5602PWBMAIN1

SONY SPRESENSE メインボード CXD5602PWBMAIN1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware



SONY SPRESENSE 拡張ボード CXD5602PWBEXT1

SONY SPRESENSE 拡張ボード CXD5602PWBEXT1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware




nice!(25)  コメント(0) 
共通テーマ:趣味・カルチャー

SPRESENSE用 CircuitPython で 9軸センサー (LSM9DS1) を動かしてみた [SPRESENSE]

CircuitPython のライブラリを探していたところ、9軸センサー(LSM9DS1)用のライブラリがあることを発見しました。LSM9DS1は、昔自作アドオンボードを作ったので試すことができます。


DSC_0996s.jpg


動かすには、adafruit のライブラリバンドルのZIPをダウンロードしてくると便利です。最新の VERSION 5.x 版を持ってきましょう。


2020-05-19.png
https://circuitpython.org/libraries


ダウンロードして展開すると様々なライブラリがありますが、今回重要なのは次の3つです。

adafruit-circuitpython-bundle-5.x-mpy-20200516
│
├── README.txt
├── VERSIONS.txt
├── examples
│   ├── lsm9ds1_simpletest.py
│
└── lib
    ├── adafruit_bus_device
    │   ├── __init__.py
    │   ├── i2c_device.mpy
    │   └── spi_device.mpy
    ├── adafruit_lsm9ds1.mpy



CircuitPython はあらかじめ SPRESENSE に書き込んでおきましょう。書き込み方は次のブログに記載されています。


SPRESENSE で "CircuitPython" を動かしてみた!
https://makers-with-myson.blog.ss-blog.jp/2020-05-08


SPRESENSEの拡張ボード側のUSBコネクタに接続すると、"CIRCUITPY:"ドライブが見えます。


DSC_0994.JPG

2020-05-19 (4).png


"adafruit_lsm9ds1.mpy" "adafruit_bus_device" はフォルダごと、"CIRCUITPY:" ドライブの ”lib” フォルダーにコピーしちゃいます。


2020-05-19 (2).png


次に、example のコード "lsm9ds1_simpletest.py" を”CIRCUITPY:”ドライブのルートに "code.py" として保存します。


2020-05-19 (6).png


"code.py" は次のように非常にシンプルです。RTOS で Python でセンサー処理って新鮮ですね。

# Simple demo of the LSM9DS1 accelerometer, magnetometer, gyroscope.
# Will print the acceleration, magnetometer, and gyroscope values every second.
import time
import board
import busio
import adafruit_lsm9ds1

# I2C connection:
i2c = busio.I2C(board.SCL, board.SDA)
sensor = adafruit_lsm9ds1.LSM9DS1_I2C(i2c)

# SPI connection:
# from digitalio import DigitalInOut, Direction
# spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
# csag = DigitalInOut(board.D5)
# csag.direction = Direction.OUTPUT
# csag.value = True
# csm = DigitalInOut(board.D6)
# csm.direction = Direction.OUTPUT
# csm.value = True
# sensor = adafruit_lsm9ds1.LSM9DS1_SPI(spi, csag, csm)

# Main loop will read the acceleration, magnetometer, gyroscope, Temperature
# values every second and print them out.
while True:
    # Read acceleration, magnetometer, gyroscope, temperature.
    accel_x, accel_y, accel_z = sensor.acceleration
    mag_x, mag_y, mag_z = sensor.magnetic
    gyro_x, gyro_y, gyro_z = sensor.gyro
    temp = sensor.temperature
    # Print values.
    print(
        "Acceleration (m/s^2): ({0:0.3f},{1:0.3f},{2:0.3f})".format(
            accel_x, accel_y, accel_z
        )
    )
    print(
        "Magnetometer (gauss): ({0:0.3f},{1:0.3f},{2:0.3f})".format(
            mag_x, mag_y, mag_z)
    )
    print(
        "Gyroscope (degrees/sec): ({0:0.3f},{1:0.3f},{2:0.3f})".format(
            gyro_x, gyro_y, gyro_z
        )
    )
    print("Temperature: {0:0.3f}C".format(temp))
    # Delay for a second.
    time.sleep(1.0)



Pythonコードを保存すると、Teraterm 等のターミナルにセンサー出力がプリントされます。これは、お手軽ですねー。


2020-05-19 (7).png


".mpy" はPythonコードなのかなと思ったのですが、開いてみるとバイナリーコードでした。そのうちどうやって作るのか探ってみたいと思います。😃
(^^)/~





SONY SPRESENSE メインボード CXD5602PWBMAIN1

SONY SPRESENSE メインボード CXD5602PWBMAIN1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware



Adafruit Trinket M0(CircuitPython/Arduino IDE用)

Adafruit Trinket M0(CircuitPython/Arduino IDE用)

  • 出版社/メーカー: スイッチサイエンス
  • メディア:







nice!(22)  コメント(0) 
共通テーマ:趣味・カルチャー

SPRESENSE用”CircuitPython”のビルド環境を作ろう!(CircuitPythonビルド編) [SPRESENSE]

いよいよ SPRESENSE向け "CircuitPython" のビルドを行います!このシリーズの完結編になります。ここでは、CircuitPython のイメージをビルドし、SPRESENSEに書き込み、PythonコードによるSPRESENSE のLチカまで行います。


python-spresense.png


【STEP1】 MicroPython のビルド


最初に”CircuitPython”のベースとなっているクロスプラットフォーム向け "MicroPython" のビルドを行います。環境の設定を失敗していなければ何事もなく終わるはずです。


$ cd circuitpython
$ make -C mpy-cross




【STEP2】 CircuitPython のビルド


次にいよいよ SPRESENSE用の CircuitPython のビルドを行います。ここが少しハマるポイントですね。SPRESENSE用のコードはなんと、cxd56 ディレクトリ配下にあります。(grep すればすぐに見つけられますが)



... @circuitpython directory から ...

$ cd ports/cxd56
$ make BOARD=spresense



SPRESENSEのプロセッサの名前は cxd5602 なので cxd56 ディレクトリにあるみたいです。これは普通分からないですよねぇ。


【STEP3】ブートローダのダウンロード(必要なひとのみ)


新たに購入した人や久しぶりにSPRESENSEを引っ張り出してきた人は、ブートローダーの更新を行う必要があります。すでに、Arduino Library や SDK で最新のブートローダーの書き込みを行っている人は必要ありません。

現在の最新のブートローダー V1.5.1 は次のURLにありますので、そこから "spresense-binaries-v1.5.1.zip" がダウンロードできます。(バージョンによってURLは異なります)


2020-05-14 (7).png
https://developer.sony.com/file/download/download-spresense-firmware-v1-5-001


ここでは仮に "C:\Users\Default\Downloads"にダウンロードしたと仮定します。

書き込みを行うために、ダウンロードしたブートローダのZIPファイルを "circuitpython/spresense-exported-sdk/firmware" に展開します。


... @circuitpython/ports/cxd56 directory から ...

$ cd spresense-exported-sdk
$ mkdir firmware
$ cd firmware
$ cp /mnt/c/Users/Default/Downloads/spresense-binaries-v1.5.1.zip .
$ sudo apt install unzip
$ unzip spresense-binaries-v1.5.1.zip
$ cd ../..




【STEP4】SPRESENSEへイメージの書込み


ここで、PCに接続したUSBケーブルをSPRESENSEをメインボード側に挿します。


DSC_0991.JPG


この時、COMの何番にSPRESENSEが接続されているか確認してください。ここでは、仮に"COM7"に接続されたとします。次のコマンドで、必要な人はブートローダーの書き込みと、CircuitPython イメージの書き込みが行えます。



... @circuitpython directory にて ...

SPRESENSEが「COM7」に接続されている前提
$ make BOARD=spresense SERIAL=/dev/ttyS7 flash-bootloader (必要な人のみ)
$ make BOARD=spresense SERIAL=/dev/ttyS7 flash 



Linuxですので、"COMxx"という名称ではハードウェアにアクセスできません。"/dev/ttySxx" と読み替えます。今回は "COM7" ですので "/dev/ttyS7" になります。これで CircuitPython の書き込みが完了しました。


【STEP5】PythonコードをSPRESENSEで動かす


USBケーブルを今度は拡張ボード側に挿しましょう。


2020-05-09.png


すると、PCに新しく "CIRCUITPY:" というドライブが見えます。


2020-05-08 (3).png


そのドライブの中に code.py というものがありますので、次のようにコードを編集し保存します。


import board
import digitalio
import time

led = digitalio.DigitalInOut(board.LED0)
led.direction = digitalio.Direction.OUTPUT

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



すると、あら不思議!コードを保存したとたんにLEDが光りだします。これはお手軽ですね!





ぜひ試してみてください!😉
(^^)/~





SONY SPRESENSE メインボード CXD5602PWBMAIN1

SONY SPRESENSE メインボード CXD5602PWBMAIN1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware



SONY SPRESENSE 拡張ボード CXD5602PWBEXT1

SONY SPRESENSE 拡張ボード CXD5602PWBEXT1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware







nice!(22)  コメント(1) 
共通テーマ:趣味・カルチャー

SPRESENSE用”CircuitPython”のビルド環境を作ろう!(ビルドツリー準備編) [SPRESENSE]

WSLの環境に、”Linuxbrew” の環境が整ったので、次はいよいよ”CircuitPython"のビルドツリーを用意したいと思います。


python-spresense.png


例によってコマンドの羅列だけで、コマンドが出力するメッセージは省略します。最初にビルドに必要なツールやライブラリをインストールします。



$ sudo apt update
$ sudo apt install git
$ sudo apt install gettext
$ sudo apt install python3-pip
$ pip3 install huffman




これが終わったら ARM用のGCCクロスコンパイラをダウンロードしてくる必要があります。昔は apt-get で入手できたのですが、そのサービスは終了したようです。次のURLから直接ダウンロードしてきてください。




GCC ARM 9-2019-q4-major version.
https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/RC2.1/gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2



ここでは仮に "C:\Users\Default\Downloads" にダウンロードしたとします。ダウンロードしたファイルを WSL の Ubuntu からコピーして展開します。ここでは bin ディレクトリを作ってそこにコピーしています。


$ mkdir bin
$ cp /mnt/c/Users/Default/Downloads/gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2 ./bin
$ cd bin
$ tar xvf gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2
$ cd ..



WSLからWindowsの各ドライブには、 "/mnt/c", "/mnt/d" という具合にアクセスできます。

これで、~/bin ディレクトリにクロスコンパイラが展開できましたので、利用できるようにパスを設定してあげます。


$ echo 'export PATH=~/bin/gcc-arm-none-eabi-9-2019-q4-major/bin:$PATH' >> ~/.profile
$ source ~/.profile
$ which arm-none-eabi-gcc
/home/ystaro/bin/gcc-arm-none-eabi-9-2019-q4-major/bin/arm-none-eabi-gcc
$



最後に"which"コマンドでパスが通じているか確認をします。これでビルドする環境はすべて整いました。いよいよ "CircuitPython" のビルドツリーを持ってきます。


$ git clone https://github.com/adafruit/circuitpython.git
$ cd circuitpython/
$ git submodule sync
$ git submodule update --init  --recursive




お疲れ様でした。これで環境面の設定はすべて完了です!😁
次はいよいよ SPRESENSE用の CircuitPython をビルドして動かしてみたいと思います!
(^^)/~





WSL構築と利用―Windows10で利用するLinux環境

WSL構築と利用―Windows10で利用するLinux環境

  • 作者: 川口 直也
  • 出版社/メーカー: カットシステム
  • 発売日: 2020/01/01
  • メディア: 単行本






SONY SPRESENSE メインボード CXD5602PWBMAIN1

SONY SPRESENSE メインボード CXD5602PWBMAIN1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware




nice!(21)  コメント(0) 
共通テーマ:趣味・カルチャー

SPRESENSE用”CircuitPython”のビルド環境を作ろう!(WSL設定編) [SPRESENSE]

SPRESENSE用"CircuitPython"のビルド環境の続きです。WSLのUbuntuのインストールが終わったら、次は開発環境を設定します。


image-3.png


今回は少し時間がかかりますので、コーヒーでも飲みながら気長にやっていきましょう。開発環境の設定は次のサイトを参考にさせていただきました。(ありがとうございます)


WSLでWindowsの中にLinuxの開発環境を作ろう
https://qiita.com/h-yoshikawa/items/15653d08f917ad6e39f8


最初に日本語化と日本時間の設定を行います。次の順番でコマンドを入力してください。(各コマンドを打った際に表示されるメッセージは省略しています)


$ sudo apt update
$ sudo apt install -y language-pack-ja
$ sudo apt install -y manpages-ja manpages-ja-dev
$ sudo update-locale LANG=ja_JP.UTF-8
$ sudo dpkg-reconfigure tzdata



ここでコンフィギュレーションの画面が出てきますので[Asia][Tokyo]に設定します。


2020-05-12.png

[Tab]キーでOKに移動できます。ちなみに日本の都市名はTokyo以外はありません…。

2020-05-12 (1).png


次からいよいよ"Linuxbrew"をインストールします。次の順番にコマンドを打ってください。
ここからは、一つ一つのコマンドにかなり時間がかかります。


$ sudo apt install build-essential curl file git

   .... 時間かかります ....

$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)"

   .... さらに時間かかります....

$ echo 'eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)' >>~/.profile
$ source ~/.profile
$



これで、"Linuxbrew" が使えるようになります。ここで一点注意があります。二つ目のコマンドが "Linuxbrew"のインストールになるのですが、次のようにインストールの途中で続けるかどうか聞いてきます。


$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)" [sudo] Enter password for xxxx to install Ruby: ==> Installing Ruby to /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor ... 省略 ... /home/linuxbrew/.linuxbrew/Caskroom /home/linuxbrew/.linuxbrew/Frameworks Press RETURN to continue or any other key to abort


ここで放って置くと、そのまま "abort" してしまいますので、時々コンソールを覗いてキー待ちになっているか見ておきましょう。

次にGCCをインストールします。


$ brew install gcc

   ... ここも時間かかります ...

$ echo 'export LDFLAGS="-L/home/linuxbrew/.linuxbrew/opt/isl@0.18/lib"' >> ~/.profile
$ echo 'export CPPFLAGS="-I/home/linuxbrew/.linuxbrew/opt/isl@0.18/include"' >> ~/.profile
$ echo 'umask 002' >> ~/.profile
$ source ~/.profile
$




これで設定完了です。最後に正しく設定されているか確認をします。


$ brew doctor
Your system is ready to brew.
$



このメッセージが出てきたら設定完了です!お疲れ様でした。😄

次はいよいよ "CircuitPython" の開発環境の設定です。
(^^)/~





WSL構築と利用―Windows10で利用するLinux環境

WSL構築と利用―Windows10で利用するLinux環境

  • 作者: 川口 直也
  • 出版社/メーカー: カットシステム
  • 発売日: 2020/01/01
  • メディア: 単行本



Ubuntuスタートアップバイブル

Ubuntuスタートアップバイブル

  • 作者: 小林 準
  • 出版社/メーカー: マイナビ出版
  • 発売日: 2018/07/30
  • メディア: 大型本



ちょっとだけLinuxにさわってみたい人のための Bash on Ubuntu on Windows入門

ちょっとだけLinuxにさわってみたい人のための Bash on Ubuntu on Windows入門

  • 作者: 中島能和
  • 出版社/メーカー: 秀和システム
  • 発売日: 2017/03/30
  • メディア: Kindle版




nice!(19)  コメント(0) 
共通テーマ:趣味・カルチャー

SPRESENSE用”CircuitPython”のビルド環境を作ろう!(WSLインストール編) [SPRESENSE]

SPRESENSE用のCircuitPythonのビルド環境にはLINUXの環境が必要です。Windows10の場合はWSL(Windows Subsystem for Linux )を使うことができます。


image-3.png


WSLはお使いの Windows10 に内蔵されており設定で機能を有効にするだけです。コントロールパネルの”プログラム”メニューの中の”Windowsの機能の有効化また無効化”を選択します。


2020-05-10.png


すると、ダイアログウィンドウが現れますのでその中から ” Windows Subsystem for Linux”の項目をチェックします。


2020-05-10 (1).png


ダイアログを閉じると再起動が促されますので再起動します。

これでまだ終わりではありません。次に”Microsoft Store”を開いて、"Ubuntu" と検索します。いくつか異なるバージョンのものが引っかかりますが、ここは迷わず "Ubuntu" を選択しましょう。 (最新版がインストールされるようです)


2020-05-11 (1).png


インストールしたのちにアイコンをクリックするとコンソールが現れユーザー名・パスワードを設定するよう促されるので、自分の好みのユーザー名・パスワードを設定すれば完了です!


2020-05-11 (2).png


しかし、Windowsでこんなに簡単にLinux(Unix)が使えるとは時代は変わりましたねぇ。
次はWSLに開発環境をセットアップしたいと思います。🙂
(^^)/~






WSL構築と利用―Windows10で利用するLinux環境

WSL構築と利用―Windows10で利用するLinux環境

  • 作者: 川口 直也
  • 出版社/メーカー: カットシステム
  • 発売日: 2020/01/01
  • メディア: 単行本



Ubuntuスタートアップバイブル

Ubuntuスタートアップバイブル

  • 作者: 小林 準
  • 出版社/メーカー: マイナビ出版
  • 発売日: 2018/07/30
  • メディア: 大型本



新しいLinuxの教科書

新しいLinuxの教科書

  • 出版社/メーカー: SBクリエイティブ
  • 発売日: 2017/06/08
  • メディア: Kindle版




タグ:ubuntu WSL Spresense
nice!(21)  コメント(0) 
共通テーマ:趣味・カルチャー

SPRESENSE用”CircuitPython”のビルド環境を作ろう!(目次編) [SPRESENSE]

前回、SPRESENSEのプリビルドで CircuitPython を動かしましたが、せっかくなのでSPRESENSE 用の "CircuitPython" のビルド環境を整備することにしました。


python-spresense.png
https://github.com/adafruit/circuitpython


SPRESENSEには、"CircuitPython" にはない機能が沢山あるので、コントリビューションするなら今がチャンスですよ!!😀

ビルド環境は、AdaFruit の CircuitPython の GitHub に記載されていますが、かなり大雑把なので、備忘録を兼ねて記録していきたいと思います。大まかには次の4ステップになります。



【STEP1】WSL (Windows Subsystem for Linux) を準備する
【STEP2】WSL(Ubuntu)に開発環境を設定する
【STEP3】CircuitPython のビルドツリーを用意する
【STEP4】CircuitPython をビルドしてSPRESENSEで動かす




STEP3以降が、やや込み入っているので後日アップしていきたいと思います!このページはそれぞれの手順へのリンクを貼っていきますので、目次として使ってください。
(^^)/~





SONY SPRESENSE メインボード CXD5602PWBMAIN1

SONY SPRESENSE メインボード CXD5602PWBMAIN1

  • 出版社/メーカー: スプレッセンス(Spresense)
  • メディア: Tools & Hardware










nice!(23)  コメント(0) 
共通テーマ:趣味・カルチャー

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。