#Leaflet ライブラリを用いて #GeoJSON を描画するテスト

結果

GeoJSON & Leaflet

M5StickCPlusでGPSロガー的なものを試作しているのですが、そこで取得したGPSデータをWordpress上で表示するためにLeafletというOpenSourceな地図ライブラリを使ってみました。

自作したGPSデータを簡単に読み込ませるのに何が良いのかも調査し、一先ずM5StickCPlusで吐き出したgpsデータをgeojsonに変換し、leafletに読み込んで使ってみる事に。で、ここで問題が発生。地図に対してレイヤーで歩いた部分のポリゴン表示をしてみたかったのですが一向にポリゴンが描画されない。小一時間悩んだ末、緯度経度を指定する順番が、geojson(経度・緯度)とleaflet(緯度・経度)で違うという事に気がつきました。思い込みは良くないですね。

下のソースコードに加えて初期化などのHTML側の設定については公式ドキュメントを参考ください。

GeoJson

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
        "properties": {
          "popupContent": "Tokyo Disney Sea"
        },
        "geometry":{
          "type": "Polygon",
          "coordinates": [
            [
              [
                139.890216,
                35.627036
              ],
              [
                139.886940,
                35.625489
              ],
              [
                139.887106,
                35.623635                
              ],
              [
                139.883258,
                35.624767
              ],
              [
                139.881992,
                35.626714
              ],
              [
                139.881450,
                35.628433
              ]
            ]
          ]
        }
      }    
  ]
}

Script

<div id="map">
<script>

var map = L.map('map').setView([139.885482, 35.626146], 16);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
	maxZoom: 20,
	attribution: '<a href=\"http://www.openstreetmap.org/copyright\">OpenStreetMap</a>'
}).addTo(map);

fetch('./leaflet/data/sample.geojson')
.then((res) => res.json())
.then((json) => {
  L.geoJSON(json, 
    {
      onEachFeature: function onEachFeature(
        feature,
        layer
      ){
        if(feature.properties && feature.properties.popupContent){
          layer.bindPopup(feature.properties.popupContent);
        }
      }
    }
  ).addTo(map);
});

</script>
</div>

余談

余談ですが、ディズニシーに家族で行くことになり、せっかくなのでついでにデータ取ってみたんですが、自作M5StickCPlusロガーの電池が即切れてしまった・・・省電力設定として液晶の輝度を下げたり、CPUクロック下げたりとやってはいたのですが、流石に外付け電池使わないと1日中使うとかは無理そうです。

#M5StickCPlus のSPIFFS領域(内部フラッシュ)を扱うシンプルなサンプルコード #Arduino

M5StickCPlusには標準でSDカードなし

センシングしたデータなどを電源が切れても保存しておくためのデータ保存について。M5StackにはSDカードが使えるようですが、M5StickCPlusには残念ながら標準では付いていません。

調べるとSPIFFSというフラッシュメモリの一部を使える事が分かりました。上記のサイトなどを参考にさせて頂きつつ、分かりにくかったところにコメントやSerial.print入れまくって自分なりに整理したものを置いておきます。

サンプルコード SPIFFS_Sample.ino

#include <M5StickCPlus.h>
#include "FS.h"
#include "SPIFFS.h"

const char *FILE_PATH = "/log.txt"; //テキスト保存するファイル名
const char *INIT_STR = ""; //初期化するテキスト;

void readFile() {

  File file = SPIFFS.open(FILE_PATH, "r");
  if (!file || file.isDirectory()) {
    Serial.printf("Failed to open file for reading: %s\r\n", FILE_PATH);
    return;
  } else {
    Serial.printf("Success to open file for reading: %s\r\n", FILE_PATH);
  }

  Serial.println("/////////////////////READ START/////////////////////");

  while (file.available()) {
    Serial.write(file.read());
  }

  Serial.println("/////////////////////READ END/////////////////////");

  file.close();
}

void removeFile() {

  //ファイルが存在すればリムーブできる
  if (SPIFFS.remove(FILE_PATH)) {

    //Serial.println("Remove success.");
    Serial.printf("Remove success: %s\r\n", FILE_PATH);
  } else {

    Serial.printf("Remove failed. Not available.: %s\r\n", FILE_PATH);
  };
}

void writeFile(const char *str) {
  File file = SPIFFS.open(FILE_PATH, FILE_APPEND);

  if (!file) {
    Serial.printf("Failed to open file for appending.: %s\r\n", FILE_PATH);
    return;
  } else {

    Serial.printf("Success to open file for appending.: %s\r\n", FILE_PATH);    

    if (file.print(str)) {

      Serial.printf("File writen: %s\r\n", str);

    } else {

      Serial.printf("File write failed: %s\r\n", str);
    }
  }

  Serial.print("File size:");
  Serial.println(file.size());

  file.close();
}


void resetFile() {

  removeFile();
  writeFile(INIT_STR);
}


void setup() {

  M5.begin();

  if (!SPIFFS.begin(true)) {
    Serial.println("SPIFFS Mount Failed");
    return;
  } else {
    Serial.println("SPIFFS Mount Success");
  }

  resetFile();
  writeFile("Test\r\n");
  writeFile("Test2\r\n");
  writeFile("139.85289,135.7654\r\n");
  readFile();
  readFile();
}

void loop() {
 
}

#M5StickCPlus + Speaker Hat でサンプル音を鳴らす Speaker.ino の改良 #Arduino

M5StickCPlus + Speaker Hat

M5StickCPlusで音を鳴らすサンプルにはArduino IDE / M5StickCPlus ライブラリ付属の Speaker.ino がありますが、ビープ音を鳴らす実装しかされていませんでした。音のデータ配列を鳴らす関数もあるのですがデータが入ってなかったので探してくっつけてみました。

見つけた音データはどうやらM5Stackの起動音的なものらしいです。探して分かったのですがバイナリデータがデカいのでサンプルコードから外されているのかなと思いました。

https://github.com/m5stack/M5Stack/blob/7e863fb9934bcb25f15afa720e9299b8a8e9a8fd/examples/Basics/FactoryTest/startup_music.c

また音を再生する上で26番PINにデータを送るのですがいろいろ試した結果 dacWrite 関数で出力するのが一番素直に再生されているようだったのでledcWriteToneから変更しています。

デモ

ボタンを押すとサンプル音が鳴りました!音質は8bitで古いサンプラーのような味のある音です。

これでM5StickCPlusに付属のMicで録音した音、Webからダウンロードした音・SDカード等から読み出した音を再生したりできそうです。

コード

#include <M5StickCPlus.h>

const int speakerPin = 26;

#define SAMPLES	(40000)
// BITSPERSAMPLE:	8,
// CHANNELS:	1,
#define SAMPLERATE	(20000)
// NORMALIZED:	FALSE,

extern const unsigned char m5stack_startup_music[];

void setup() {

    // put your setup code here, to run once:
    M5.begin();
    M5.Lcd.setRotation(0);
    M5.Lcd.setCursor(25, 80, 4);
    M5.Lcd.println("speaker");
    
}

void playMusic(const uint8_t* music_data,uint16_t sample_rate) {
    
    uint32_t length         = strlen((char*)music_data);
    uint32_t delay_interval = ((uint32_t)1000000 / sample_rate);
    for (int i = 0; i < length; i++) {
        
        dacWrite(speakerPin,music_data[i]);
        delayMicroseconds(delay_interval);
    }

}

void loop() {

  M5.update();

  if(M5.BtnA.wasReleased()){

    M5.Lcd.setCursor(25, 80, 4);
    M5.Lcd.println("playing!");

    //音楽の再生
    playMusic(m5stack_startup_music,SAMPLERATE);
    
    M5.Lcd.setCursor(25, 80, 4);
    M5.Lcd.println("speaker");

  }
   
}

// music from https://github.com/m5stack/M5Stack/blob/7e863fb9934bcb25f15afa720e9299b8a8e9a8fd/examples/Basics/FactoryTest/startup_music.c
const unsigned char m5stack_startup_music[] = {

 //ここに上のリンクの配列データを貼り付ける

 };

#M5StickCPlus でビープを鳴らす

M5Stackがアツいらしい

ちょいと試作をしてみようと思い久しぶりにArduino界隈に触れてみました。M5 Stackという開発環境が流行っているようなので上記の本を元に勉強を進めています。

購入したのはM5 StickC Plus

用途的に細くて軽い方が良かったのでSwitch ScienceにてM5 StickC Plusという製品を買いました。本を進めていると搭載Beep音を鳴らす所でM5 Stackのコードでは単純には動かないものがあり、M5 StickC Plusでは少し書き方が違うので調べました。

M5以下のSpeakerクラスが実装されておらず、代わりにBeepクラスが実装されているようでした。

修正後動いた!

コード(上記の本のサンプルコードのM5 Stick C Plus用 改変版)

#include <M5StickCPlus.h>

//各音符の周波数を定義しています
#define NOTE_C4 261.626  //ド4 C4
#define NOTE_C4b 277.183 //ド#4 C#4
#define NOTE_D4 293.665  //レ4 D4
#define NOTE_D4b 311.127 //レ#4 D#4
#define NOTE_E4 329.628  //ミ4 E4
#define NOTE_F4 349.228  //ファ4 F4
#define NOTE_F4b 369.994 //ファ#4 F#4
#define NOTE_G4 391.995  //ソ4 G4
#define NOTE_G4b 415.305 //ソ#4 G#4
#define NOTE_A4 440.000  //ラ4 A4
#define NOTE_A4b 466.164 //ラ#4 A#4
#define NOTE_B4 493.883  //シ4 B4
#define NOTE_C5 523.251  //ド5 C5
#define NOTE_C5b 554.365 //ド#5 C#5
#define NOTE_D5 587.330  //レ5 D5
#define NOTE_D5b 622.254 //レ#5 D#5
#define NOTE_E5 659.255  //ミ5 E5
#define NOTE_F5 698.456  //ファ5 F5
#define NOTE_F5b 739.989 //ファ#5 F#5
#define NOTE_G5 783.991  //ソ5 G5
#define NOTE_G5b 830.609 //ソ#5 G#5
#define NOTE_A5 880.000  //ラ5 A5
#define NOTE_A5b 932.328 //ラ#5 A#5
#define NOTE_B5 987.767  //シ5 B5
#define NOTE_C6 1046.502 //ド6 C6

//音符の長さをミリ秒で定義しています
#define R_4 2800
#define R_3 2100
#define R_2 1400
#define R_1 700
#define R_1_2 350
#define R_1_3 233.3
#define R_1_4 175
#define R_1_6 116.6
#define R_1_8 87.5
#define R_1_12 58.3
#define R_1_16 43.75

//エルガー「愛の挨拶」のメロディ
float salut[][2] =
{
  {NOTE_G5b, R_1},   {NOTE_B4, R_1_2}, {NOTE_G5b, R_1_2},
  {NOTE_F5b, R_1_2}, {NOTE_E5, R_1_2}, {NOTE_D5b, R_1_2}, {NOTE_E5, R_1_2},
  {NOTE_A5, R_1},   {NOTE_A5, R_1}, {NOTE_A5, R_1},    {NOTE_B4, R_1_2}
};

void setup() {
  M5.begin();
}

void loop() {
  //ボタンA(左のボタン)が押されたとき
  if (M5.BtnA.wasPressed()) {
    //スピーカーからブザー音を鳴らします
    M5.Beep.beep();
  }
  //ボタンB(中央のボタン)が押されたとき
  if (M5.BtnB.wasPressed()) {
    //配列salutを順番に読み込みます
    for (int i = 0; i < sizeof(salut) / sizeof(salut[0]); i++) {
      //[i][0]番目の要素の周波数をスピーカーから鳴らします。
      M5.Beep.tone(salut[i][0]);
      //[i][1]番目の要素の時間待ちます(音符の長さ)
      delay(salut[i][1]);
    }
    M5.Beep.end();
  }

  // M5Stackのボタン操作やスピーカーの状態を更新します
  M5.update();
}

Node for Max 上で tonal ライブラリを利用して Key Scaler を試作 #max8 #n4m

Midi NoteのPitchを特定のスケールに矯正

cycling’74のN4Mサンプルにも入っていた tonal をnode objectで使ってみたのでメモです。

鍵盤の音を特定のスケール(とりあえずC Major)に矯正するスクリプトとなっております。Node for Maxのご参考にどうぞ。

tonal.js

引用 “tonal is a music theory library. Contains functions to manipulate tonal elements of music (note, intervals, chords, scales, modes, keys). It deals with abstractions (not actual music or sound).

tonal is implemented in Typescript and published as a collection of Javascript npm packages.

It uses a functional programing style: all functions are pure, there is no data mutation, and entities are represented by data structures instead of objects.”

https://github.com/tonaljs/tonal ※導入方法はこちら参照

keyscaler.js


const MAX_API = require("max-api");
const { Scale } = require("@tonaljs/tonal");
const { Key } = require("@tonaljs/tonal");
const { Mode } = require("@tonaljs/tonal");
const { Midi } = require("@tonaljs/tonal");
const { Note } = require("@tonaljs/tonal");
const { Chord } = require("@tonaljs/tonal");

const notes = Scale.get("C major").notes;
console.log("notes:"+notes);

let midiNotes = notes.map((value) => {
    return Note.midi(value+"0")
});
console.log("midiNotes:"+midiNotes);

const handlers = {
    "getScaledPitch" : (pitch) => {

        let returnPitch = pitch;

        while(
            midiNotes.every(value => {
                return ((value % 12) != (returnPitch % 12))
            })   
        ){
            returnPitch++;            
        };

        //MAX_API.post(pitch + "->" + returnPitch);

        MAX_API.outlet(returnPitch);  
    },
    /*
    [maxAPI.MESSAGE_TYPES.BANG] : () =>{
        console.log("bang");
    },
    */
};

MAX_API.addHandlers(handlers);