SPRESENSE用のXMODEMライブラリを作ってみた! [SPRESENSE]
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
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE カメラモジュール CXD5602PWBCAM1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE LTE拡張ボード CXD5602PWBLM1J
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア:
【朗報】SPRESENSE の CircuitPython が "GPS"をサポート! [SPRESENSE]
■ 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" を発見!
早速、最新のツリーをCloneしてビルドしてみました。ビルドの仕方はこちらを参照してください。
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。位置情報が得られるようになると色々とアプリケーションを作る気が湧いてきます。
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 トラッカーが出来上がりそうです。
https://www.switch-science.com/catalog/5374/
これからのアップデートをさらに期待したいところですね!
( ^ω^ )/~
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE 拡張ボード CXD5602PWBEXT1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SPRESENSEスペクトログラムをアップデート!! [SPRESENSE]
実際の動きはこちらをご覧ください。
Spresense Spectrogram アップデートしました。
— よしのたろう (@Taro_Yoshino) August 18, 2020
FFTのポイント数は256~4096まで対応してますが、LCDの画面の都合上256にしました。Spresenseの計算能力はまだまだ余裕があるので、もっと高解像度いけそうです。 pic.twitter.com/6VNqNAttKA
スケッチはこちら。解析はメインコアで、表示はサブスコアで行っています。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
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE 拡張ボード CXD5602PWBEXT1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
Voice Assistant Using SONY Spresense (English Edition)
- 作者: Perez, Guillermo
- 出版社/メーカー:
- 発売日: 2019/03/11
- メディア: Kindle版
SPRESENSE でスペクトログラム出力をしてみた [SPRESENSE]
画質は悪いですが、動作している様子をTwitterにあげました。そのうちYoutubeにもあげたいと思います!
Spresense の Spectrogram出力。表示を少し高速化(とは言っても20msecは大きい)。いい感じで出力できるようになってきた。ただ、時々線が出ているのが気になる。もう少し解析が必要だな。 pic.twitter.com/uSbYSpPO7M
— よしのたろう (@Taro_Yoshino) August 4, 2020
このプログラムは、最近新しく追加された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)
- 作者: 青木 直史
- 出版社/メーカー: 技術評論社
- 発売日: 2013/02/01
- メディア: 単行本(ソフトカバー)
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE 拡張ボード CXD5602PWBEXT1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SPRESENSE用 CircuitPython で 9軸センサー (LSM9DS1) を動かしてみた [SPRESENSE]
動かすには、adafruit のライブラリバンドルのZIPをダウンロードしてくると便利です。最新の VERSION 5.x 版を持ってきましょう。
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:"ドライブが見えます。
"adafruit_lsm9ds1.mpy" と "adafruit_bus_device" はフォルダごと、"CIRCUITPY:" ドライブの ”lib” フォルダーにコピーしちゃいます。
次に、example のコード "lsm9ds1_simpletest.py" を”CIRCUITPY:”ドライブのルートに "code.py" として保存します。
"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 等のターミナルにセンサー出力がプリントされます。これは、お手軽ですねー。
".mpy" はPythonコードなのかなと思ったのですが、開いてみるとバイナリーコードでした。そのうちどうやって作るのか探ってみたいと思います。😃
(^^)/~
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
Adafruit Trinket M0(CircuitPython/Arduino IDE用)
- 出版社/メーカー: スイッチサイエンス
- メディア:
SPRESENSE用”CircuitPython”のビルド環境を作ろう!(CircuitPythonビルド編) [SPRESENSE]
【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は異なります)
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をメインボード側に挿します。
この時、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ケーブルを今度は拡張ボード側に挿しましょう。
すると、PCに新しく "CIRCUITPY:" というドライブが見えます。
そのドライブの中に 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が光りだします。これはお手軽ですね!
おっ、SPRESENSE で CircuitPython 動いた!Lチカ成功!
— よしのたろう (@Taro_Yoshino) May 7, 2020
CircuitPython on Spresense is working!! Blinking the LED0 is successful.
思ったよりも簡単だったな。拡張ボード必須というのがイマイチだけど。LTE-M拡張ボードで試してみたいな。#Spresense #CircuitPython #Adafruit pic.twitter.com/VKQcTOsqcU
ぜひ試してみてください!😉
(^^)/~
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE 拡張ボード CXD5602PWBEXT1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SPRESENSE用”CircuitPython”のビルド環境を作ろう!(ビルドツリー準備編) [SPRESENSE]
例によってコマンドの羅列だけで、コマンドが出力するメッセージは省略します。最初にビルドに必要なツールやライブラリをインストールします。
$ 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環境
- 作者: 川口 直也
- 出版社/メーカー: カットシステム
- 発売日: 2020/01/01
- メディア: 単行本
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SPRESENSE用”CircuitPython”のビルド環境を作ろう!(WSL設定編) [SPRESENSE]
今回は少し時間がかかりますので、コーヒーでも飲みながら気長にやっていきましょう。開発環境の設定は次のサイトを参考にさせていただきました。(ありがとうございます)
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]に設定します。
[Tab]キーでOKに移動できます。ちなみに日本の都市名はTokyo以外はありません…。
次からいよいよ"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環境
- 作者: 川口 直也
- 出版社/メーカー: カットシステム
- 発売日: 2020/01/01
- メディア: 単行本
ちょっとだけLinuxにさわってみたい人のための Bash on Ubuntu on Windows入門
- 作者: 中島能和
- 出版社/メーカー: 秀和システム
- 発売日: 2017/03/30
- メディア: Kindle版
SPRESENSE用”CircuitPython”のビルド環境を作ろう!(WSLインストール編) [SPRESENSE]
WSLはお使いの Windows10 に内蔵されており設定で機能を有効にするだけです。コントロールパネルの”プログラム”メニューの中の”Windowsの機能の有効化また無効化”を選択します。
すると、ダイアログウィンドウが現れますのでその中から ” Windows Subsystem for Linux”の項目をチェックします。
ダイアログを閉じると再起動が促されますので再起動します。
これでまだ終わりではありません。次に”Microsoft Store”を開いて、"Ubuntu" と検索します。いくつか異なるバージョンのものが引っかかりますが、ここは迷わず "Ubuntu" を選択しましょう。 (最新版がインストールされるようです)
インストールしたのちにアイコンをクリックするとコンソールが現れユーザー名・パスワードを設定するよう促されるので、自分の好みのユーザー名・パスワードを設定すれば完了です!
しかし、Windowsでこんなに簡単にLinux(Unix)が使えるとは時代は変わりましたねぇ。
次はWSLに開発環境をセットアップしたいと思います。🙂
(^^)/~
WSL構築と利用―Windows10で利用するLinux環境
- 作者: 川口 直也
- 出版社/メーカー: カットシステム
- 発売日: 2020/01/01
- メディア: 単行本
SPRESENSE用”CircuitPython”のビルド環境を作ろう!(目次編) [SPRESENSE]
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
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
覚えないで学ぶMicroPythonプログラミング: プログラミングを知らない人に贈る「ずるい」プログラミングの本
- 作者: 石岡 智明
- 出版社/メーカー:
- 発売日: 2019/07/01
- メディア: Kindle版