月: 2023年1月

苺栽培マシーン

苺収穫直前

苺栽培マシーンで養液栽培している苺の第一号が順調に育って、いよいよ収穫直前という感じになってきました。12/28 に花が咲いたので、大体40日くらいでここまで来た感じです。我ながらなかなか順調じゃないかなと思います。

隣にも実が写っているように他の花や実も順調で、現時点で50個ほど花と実ができてきています。これから続々と赤い実がなっていくかと思うと楽しみです。

苺栽培マシーン, 電子工作

ECメータを作る(2)交流電圧を安定させる

ECメータを作る(1)交流電圧を作るで交流電圧を作るところまではできましたが、このままではウィーンブリッジ発振回路の先に繋げる回路によっては出力が安定しません。こういう時には出力を強化するためにエミッタフォロワ回路を挟みます。

エミッタフォロワ回路にも各種あり、オーディオ用途などのように信号の歪を極力減らしたいような場合はプッシュプル型のものを作成したりしますが、今回は電圧を測りたいだけなので簡単に実現できる方法を採用します。

注意点としては現時点ではVddが+5Vと想定しているので、正弦波の振幅が5V(実際はもう少し狭い)を超えると信号が大きく歪んでしまうことです。作成した交流電圧は±4Vくらいあるのでこのままではダメですね。この場合はエミッタフォロワ回路に与える電圧を上げるか交流電圧の振幅を狭める必要があります。今回は、値の測定に問題が無さそうなら振幅を狭める方向で対応しようかと思っています。

回路の設計ができたので、ブレッドボード上に実装して計測してみます。

入力と出力を比較した結果は以下の通りです。上が入力波形、下が出力波形です。入力と全く同じ波形が出力できています。

ちなみに、入力波形はPCのヘッドホン出力から1KHzの正弦波を出力して使用しています。矩形波だと高周波が削られてあまり良い出力が得られないですが、これくらいのものであれば十分使えます。

ヘッドホン出力から正弦波が取れるなら、負電源とかウィーンブリッジ回路とかいらないんじゃ・・・?と思ったアナタ。するどいですね~。ヘッドホン出力をECメータのために使用して良いのであればこれで十分なんじゃないかと思っています。

VisionFive2, 電子工作

A/Dコンバータの値を取得する

水位センサを買うのと一緒にスイッチサイエンスで”GROVE – ADS1115搭載 16bit ADC – 4チャンネル“を購入したので、こちらにも対応します。こちらの商品は Grove やスクリューターミナルがついてて便利なものの1760円とそこそこのお値段がするので、同じチップを搭載したもうちょっと安いものを探してきて使った方が良かったかな~とおもいます。

ADS1115 自体に I2C の機能も搭載されていて、このボードは単にチップと端子を繋いでいるだけのようです。Linuxは標準で ADS1115 に対応しており、VisionFive2 SDK でも VisionFive2/linux/drivers/iio/adc/ti-ads1015.c があります。これとデータシートを参考にしながら作業を進めます。また、”[電子工作]ADS1115 16bit ADCを使ってみた“で ADS1115 の使い方が説明されているのでここのコードも参考にしました。

ちなみに、Linux で標準対応しているならカーネルモジュールを用意すれば Linux 方式で使えるのでは!?!?!?と思ってやってみたのですが、カーネルモジュールのビルドとロードはできたもののこれを使うには device tree への登録が必要で、そのためには u-boot の更新が必要で、オリジナルの u-boot を使うためには自前で用意した sdcard.img を使うようにしなければいけないけど VisionFive2 SDK から作ることができるのは最低限の環境なのでどーする!?!?というところで詰まってしまいました。。。Linux よくわからんちん。。。

コード

でまあ何やかんやと四苦八苦しながら書いたのが以下のコードです。基本は動画で説明されている内容そのままで、VisionFive2 が対応していない Arduino の Wire.h 関連を”水位センサの値を取得する“でも参考にした”LinuxのC言語でI2Cデバイスと通信する“を参考に書いています。細かい説明は。。。精も根も尽き果てたのでご勘弁を_(:3 」∠ )_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdint.h>

#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

//i2c address
#define I2C_DEVICE         "/dev/i2c-0"
#define ADS1115_I2C_ADDRESS 0x48

//BIT[1:0] Register address
#define CONVERSION_REGISTER 0
#define CONFIG_REGISTER     1
#define LO_THRESH_REGISTER  2
#define HI_THRESH_REGISTER  3

//BIT[15]
#define NO_EFFECT              (0 << 15)
#define START_SINGLE_CONVERSION (1 << 15)

//BIT[14:12] Input multiplexer configuration
#define P_AIN0_N_AIN1 (0 << 12)
#define P_AIN0_N_AIN3 (1 << 12)
#define P_AIN1_N_AIN1 (2 << 12)
#define P_AIN2_N_AIN1 (3 << 12)
#define P_AIN0_N_GND  (4 << 12)
#define P_AIN1_N_GND  (5 << 12)
#define P_AIN2_N_GND  (6 << 12)
#define P_AIN3_N_GND  (7 << 12)

//BIT[11:9] Programmable gain amplifier configuration
#define FSR_6144MV (0 << 9)
#define FSR_4096MV (1 << 9)
#define FSR_2048MV (2 << 9)
#define FSR_1024MV (3 << 9)
#define FSR_0512MV (4 << 9)
#define FSR_0256MV (5 << 9)
//#define FSR_0256MV (6 << 9)
//#define FSR_0256MV (7 << 9)

//BIT[8] Device operating mode
#define CONTINUOUS_CONVERSION (0 << 8)
#define SINGLE_SHOT           (1 << 8)

//BIT[7:5] Data rate
#define DATA_RATE_8_SPS   (0 << 5)
#define DATA_RATE_16_SPS  (1 << 5)
#define DATA_RATE_32_SPS  (2 << 5)
#define DATA_RATE_64_SPS  (3 << 5)
#define DATA_RATE_128_SPS (4 << 5)
#define DATA_RATE_250_SPS (5 << 5)
#define DATA_RATE_475_SPS (6 << 5)
#define DATA_RATE_860_SPS (7 << 5)

//BIT[4] Comparator mode
#define TRADITIONAL_COMPARATE (0 << 4)
#define WINDOW_COMPARATE      (1 << 4)

//BIT[3] Comparator polarity
#define ACTIVE_LOW  (0 << 3)
#define ACTIVE_HIGH (1 << 3)

//BIT[2] Latching comparator
#define NONLATCHING_COMPARATOR (0 << 2)
#define LATCHING_COMPARATOR    (1 << 2)

//BIT[1:0] Comparator queue and disable
#define ASSERT_AFTER_1_CONVERSION 0
#define ASSERT_AFTER_2_CONVERSION 1
#define ASSERT_AFTER_4_CONVERSION 2
#define DISABLE_COMPARATOR        3

#define LSB_SIZE_6144MV (187.5    * 1E-6)
#define LSB_SIZE_4096MV (125.0    * 1E-6)
#define LSB_SIZE_2048MV ( 62.5    * 1E-6)
#define LSB_SIZE_1024MV ( 31.25   * 1E-6)
#define LSB_SIZE_0512MV ( 15.625  * 1E-6)
#define LSB_SIZE_0256MV (  5.8125 * 1E-6)

void printb(unsigned int v) {
  int buf[16];
  for(int i=0;i<16;i++) {
    buf[15-i] = (v >> i) & 1;
  }

  for(int i=0;i<16;i++) {
    printf("%d", buf[i]);
  }
}

int32_t i2c_open(char *i2c_device) {
  int32_t fd = open(I2C_DEVICE, O_RDWR);
  if (fd == -1) {
    fprintf(stderr, "i2c_read: failed to open: %s\n", strerror(errno));
    return -1;
  }

  return fd;
}

void i2c_close(int32_t i2c_dev) {
  close(i2c_dev);
}

int8_t i2c_read(int32_t i2c_dev, uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint16_t length) {
  struct i2c_msg messages[] = {
      { dev_addr, 0, 1, &reg_addr},
      { dev_addr, I2C_M_RD, length, data },
  };
  struct i2c_rdwr_ioctl_data ioctl_data = { messages, 2 };

  if (ioctl(i2c_dev, I2C_RDWR, &ioctl_data) != 2) {
    fprintf(stderr, "i2c_read: failed to ioctl: %s\n", strerror(errno));
    return -1;
  }

  return 0;
}

int8_t i2c_write(int32_t i2c_dev, uint8_t dev_addr, uint8_t reg_addr, const uint8_t* data, uint16_t length) {
  uint8_t* buf = (uint8_t*)malloc(length + 1);
  if (buf == NULL) {
    fprintf(stderr, "i2c_write: failed to memory allocate\n");
    return -1;
  }
  buf[0] = reg_addr;
  memcpy(&buf[1], data, length);
  struct i2c_msg messages[] = {
    { dev_addr, 0, length+1, buf}
  };
  struct i2c_rdwr_ioctl_data ioctl_data = { messages, 1 };

  if (ioctl(i2c_dev, I2C_RDWR, &ioctl_data) != 1) {
    fprintf(stderr, "i2c_write: failed to ioctl: %s\n", strerror(errno));
    free(buf);
    return -1;
  }

  free(buf);
  return 0;
}

uint16_t ads1115_read_register(int32_t i2c_dev) {
  uint8_t buf[2];
  int16_t data;

  i2c_read(i2c_dev, ADS1115_I2C_ADDRESS, CONVERSION_REGISTER, buf, 2);

  data = (buf[0] << 8) | buf[1];
  return data;
}

void ads1115_write_register(int32_t i2c_dev, uint16_t data) {
  uint8_t buf[] = {
    data >> 8 & 0xff,
    data >> 0 & 0xff
  };
  i2c_write(i2c_dev, ADS1115_I2C_ADDRESS, CONFIG_REGISTER, buf, 2);

  return;
}

int init(int32_t i2c_dev) {
  ads1115_write_register(i2c_dev,
                         NO_EFFECT |
                         P_AIN0_N_GND |
                         FSR_6144MV |
                         CONTINUOUS_CONVERSION |
                         DATA_RATE_8_SPS |
                         WINDOW_COMPARATE |
                         ACTIVE_LOW |
                         LATCHING_COMPARATOR |
                         ASSERT_AFTER_1_CONVERSION);

  return 0;
}

int sense(int i2c_dev) {
  int16_t sensor_val;

  sensor_val = ads1115_read_register(i2c_dev);

  printf("sensor value: ");
  printb(sensor_val);
  printf("\n");

  printf("voltage = %f\n", sensor_val * LSB_SIZE_6144MV);
}

int main() {
  int32_t i2c_dev;

  i2c_dev = i2c_open(I2C_DEVICE);

  init(i2c_dev);

  while(1) {
    sense(i2c_dev);
    sleep(1);
  }

  i2c_close(i2c_dev);

  return 0;
}

動作確認

直流安定化電源の出力を入力して、電圧を変えながら様子を見ます。

それでは実行。

chiyama@starfive:~$ sudo ./ADS1115
sudo: unable to resolve host starfive: Name or service not known

voltage = 0.000188
sensor value: 0000010011100100
voltage = 0.234750
sensor value: 0001000111001010
voltage = 0.853875
sensor value: 0001110101111011
voltage = 1.415063
sensor value: 0010100100101011
voltage = 1.976063
sensor value: 0010110110000011
voltage = 2.184563
sensor value: 0011000110100101
voltage = 2.382938
sensor value: 0100000101011101
voltage = 3.137437
sensor value: 0100101100111100
voltage = 3.611250
sensor value: 0101000010110011
voltage = 3.873563
sensor value: 0101000011010001
voltage = 3.879188
sensor value: 0101000011010100
voltage = 3.879750
sensor value: 0101000011010101
voltage = 3.879938
sensor value: 0101000011010110
voltage = 3.880125
sensor value: 0101000011011000
voltage = 3.880500
sensor value: 0101000011010100
voltage = 3.879750


^C
chiyama@starfive:~$

直流安定化電源の電圧を4Vまで上げたところ、3.88Vで計測されました。ちょっと低いですね。3.8Vくらいまではかなり正確な値が計測しているのですが、そこから先が誤差が広がっているようです。VDDに近くなるとダメなのか???と思いつつ、よみやさんの動画では4.18Vまで計測しているのでそういうわけでもなさそうです。データシートも目を通してみたものの、それっぽい制限事項を見つけることができなかったです。。。謎な。。。このあたりは、私の電子工作関連のスキルの低さが現れてしまっている感じです(涙

とはいえ、3.8Vまでであればいい感じに使えるようなので一旦はこれで良しとしておきます。

苺栽培マシーン, 電子工作

ECメータを作る(1)交流電圧を作る

苺栽培マシーンで苺を効率よく成長させるためには肥料の濃度を適切に保つ必要があります。この管理に使用されるのがECメータです。ECメータを使用することで養液の中にどの程度の不純物が溶け込んでいるかを測ることができます。ただし、ECメータは溶液の抵抗値を見ているだけなのでどのような成分がどのような割合で溶けているのかということまでは確認することはできないです。

養液の抵抗値を測るだけなので、溶液中に電極を配置して電気を流せば値を取得できます。このとき、単純に直流電圧をかけると片側の電極に溶液中のイオンが引き寄せられて結晶化してしまい、センサーの性能が低下してしまいます。これを避けるために電極には交流電圧をかけます。

交流電圧をつくる

オペアンプを使用したウィーンブリッジ回路を使用して交流電圧を作ることができます。オペアンプを使用するためには負電圧が必要になりますが、”オペアンプ用に負電圧を作る“で作成しているのでそれを使います。

ウィーンブリッジ回路についてはCQ出版社の”ウィーン・ブリッジ発振回路が適切に発振する抵抗値はいくら?“を参考にしました。

ウィーンブリッジ発振回路

設計ができたらブレッドボード上に回路を組んで動作確認します。写真の右側が負電圧の生成回路で真ん中がウィーンブリッジ回路です。

手元の環境では回路図のままではいい感じに発振してくれなかったのでR2の部分に可変抵抗を挟んでいい感じの波形が出るように調整が必要でした。調整して得られた波形は下図の通りです。

綺麗な正弦波が出力されています。

元の電源が5Vなので±5Vの正弦波が欲しいところですがそれは難しそうです。また、0.15Vくらいマイナス寄りになっているのがちょっと気になります。うーん。これは何なんでしょうね?とりあえず今のところはヨシとしておきます。

色々雑なところはあるものの、これで計測に必要な交流電圧を得ることができました。

工作

CNCフライス盤が欲しい

実は私は長年患っている病気があって、記憶にあるだけでも社会人になって早々に発症しているのでもう四半世紀くらいの付き合いになります。

その名も、CNCフライス盤欲しい病です。数年に一度発症しては猛威を振るい、調査と妄想のために無限に時間を溶かしては何とか抑えるということを繰り返してきました。Twitter をちょっと調べただけでも 2012, 2014, 2020, 2021, 2022 年と発症しているようです。特にここ数年は毎年猛威を奮っているようで、我ながら呆れてしまいます。インフルエンザかよっ!!

そんな感じで2023年も新年早々発症して時間を無限に溶かしながらCNCフライス事情を調べています。せっかくなので2023年1月現在の状況をざっくりとまとめてみます。

注意

私は単にCNCフライス盤欲しい病にかかった患者というだけの素人です。ここに書いてある内容も本職の人が読んだら噴飯ものの内容もあるかと思います。もしそういう内容を見つけたらツッコミを頂けるとありがたいです。

前提条件

CNCフライスで今すぐ何か作りたいものがあるというわけではなく、単にCNCフライスが欲しいというのが主な動機です。ただ、モータを使ってガッチャンガッチャン動くものは作りたいので、金属、できれば鉄やステンレスといった硬い素材も扱うことができると嬉しいなと思っています。強いて言うと3Dプリンタが作り途中で放置したままなので、そのためのパーツは作りたいなぁとは思っています。

あとは、本格的に仕事として何かを作らないといけないというわけではない上にスペースもそれほど無いので卓上サイズのものを探しています。

以下、この条件で調査を進めた結果をまとめていきます。

格安中華CNC

趣味レベルでCNCフライスを購入する場合に真っ先に候補に挙がるのが Aliexpress などで購入できる格安中華CNCになります。価格は数百~$1000くらいのレンジで、品質ガチャが激しいようで到着した品物が動けばラッキー、まずは基板を見て部品が外れていないか確認するところから始まるようです。

精度もホビーユースならまあいいかというレベルではあるものの、溢れる手作り感と改造のしやすさから自分の手で育てていく楽しみにハマる人が続出する、ある意味非常に魅力的なジャンルとなっています。

omnio cnc

格安中華CNCの先駆け(?)で、他の格安中華CNCはこのメーカーのパクりが多いようです。格安中華CNCは作ったメーカー名もわからないものが多い中、メーカー名がわかる数少ないところです。

パーツもそれなりのものを使用し、カタログ上に精度の記載もあります。メーカー名で検索すると日本人でも購入して使用している人がそこそこいるようで、そういう意味でもちょっと安心感があります。$1700~ と中華 CNC の中ではちょっとお高めになります。X4-800L-USB を購入している人が多い印象がありますが、三軸ともリニアガイドレールを採用しているのは X6-1500L-USB 以上、リニアガイドレールの型番までわかるのは X6-2200L-USB なのでできればこれの方がいいのかなーと思います。ただし、$2500 するので中華CNCにそこまでかけるか???という気になってくるお値段です。

また、リニアガイドレールの型番は書かれているもののメーカー名がわからないので購入の際は確認をした方が良いかもしれないです。HIWIN製かと思っていたら全然違うメーカーだったという例もあるようです。それから、位置決め精度が 0.05mm なので、これが十分なのかどうかは要確認です。もうちょっと良いものだと0.01mmというものが普通にあるので、30万円かけて買うならもうちょっと出して良いものを買おうかな~?という気になってしまいます。

ChinaCNC(1/16追加)

6/27追記 : ChinaCNCはサポート体制がかなり良くないらしく、本体に致命的な問題があってもまともに対応してもらえなかったというケースが結構あるようです。コントローラの回路でパーツが外れていたくらいなら直せばいいかと思っていたものの、組み立てられないとか本体が歪んでいてそもそも真っすぐが出ないという話があると、いくら中華製とは言え手を出すのはかなりリスキーだなという印象を受けます。

自社で機械を製造しているっぽいところでもう一つ、ChinaCNC があります。こちらはYouTubeにもチャンネルがあり、たくさんの動画がアップされているため安心感があります。後述するlycncで販売されている縦型CNCも元を辿るとここが作っているようです。更に、縦型機の新型が出ているようで、こちらならそれを購入することができます。こちらだと3040サイズの5軸で1500Wスピンドルモーター仕様で $3000 とのことです。

また、金属対応を謳った門型のものも取り揃えています。こちらは本体がステンレスで作られていて位置決め精度も0.01mmとかなり良さそうです。購入サイトを見るとXYZ軸ともにリニアガイドレールを使用しているということらしいですが、Technical detail では”XYZ rail material : Hard chrome shaft”と書いてあって微妙に謎です(リニアガイドレールモデルとそうでないものの両方があるっぽい?感じです)。リニアガイドレールのメーカーなど詳細仕様は要確認ですが、金属加工対応、リニアガイドレール搭載、クローズドループモーター搭載で $2700 (ステッピングモーター仕様なら$2450)ならかなり魅力的です。

この他にもアルミ筐体でリーズナブルなものも揃っているので、そこまで高性能でなくても良いよ!!っていう人にも良さそうです。

LY.GROUP.CHINA

もう一つ中華CNCで目に付くのが LY.GROUP.CHINA(lycnc)です。Webサイトを見るとかなりの種類を出していて大きな会社っぽく見えるものの、何というか全体的に漂うチープさとか写真の嘘くささが怪しさ満点です。

※1/16追記:lycncは自社で製造しているというより、代理店っぽい立ち位置なのかもしれないです。そのため、ここで販売している製品は探せば開発元がみつかるかもしれません。

極めつけは、Webサイトに載っている会社の住所をGoogle Mapで見ると建物一つないただの水田?で、最初に確認したときは目が点になりました。

LY.GOUP.CHINA の所在地

じゃあ詐欺かというとそんなこともなく、@roughroboticsさんが実際に購入して動かしている事例(中華CNCフライス買ってみた!#01 性能はいかに!?【CeVIO】)もあるので大丈夫そうな感じがします。中国謎すぎる。。。

私はここの 4軸CNC 6040 1500 2.2kwリニアガイドレール金属彫刻機工作機械3040 (ハンドホイール付き) が良さげだなと思っています。4軸USBポート、1500Wスピンドルで300x400mmで$1700くらい、各軸リニアガイドレールを使用して位置決め精度が0.03mmということで omnio cnc よりもバランスが良さそうです。また、他の製品は筐体がアルミ製なのに対し、これはスチール製なのも特筆すべき点です。ちなみにこちらもリニアガイドレールのメーカーはわからないので、購入の際は確認した方が良さそうです。

もうちょっと良いものを求めるなら縦型の”Cncルータ5軸2200ワット金属彫刻機cnc 6040 usb cncフレーム3040柱型木材アルミ銅フライス機“が良さそうです。$2000~とちょっと高くなりますが、位置決め精度が 0.01mm となっています。作業領域も 300x400x150mm と、門型に比べても遜色ないです。

5軸で各種オプションをモリモリにしても$2500くらいなので、(カタログスペックが確かなら)かなりアリな気がします。→ChinaCNCが製造元っぽいので、買うならそちらの方が良さそうです

日本のCNCフライス

日本で販売されているリーズナブルなCNCフライスは中国メーカーのものを日本で整備・改造したものが多いです。一番目に付くのが旋盤市場PSF240-CNCPSF550-CNCです。中華CNCでよくある門型の筐体ではなく、縦型で剛性も確保されている上に日本でのサポートがあるのが魅力です。PSF240-CNCであれば25万円から購入できます。まあ、必要なオプションをつけると40万円くらいにはなると思いますが。笑。

PSF240-CNC

DIYでCNC化

CNC化されていないものであれば旋盤市場、寿貿易東洋アソシエイツから同じメーカーのものが独自に整備されたものが販売されているので、これを自分で改造してCNC化する人たちも結構います。ただ、この場合は改造のためのパーツ造りに旋盤やフライス盤が必要になるので一台目のCNCフライス盤が欲しい、私のような人にはちょっとハードルが高いです。

MAKERA: Carvera

2021年10月にKickstarterでクラウドファンディングが実施され、2022年8月に出荷予定だったプロダクトです。プロジェクトは順調に遅れたものの、2023年1月、正に今のタイミングでユーザーの手元に届き始めているようです。

Carvera はとても意欲的なプロダクトで、フルエンクロージャ、ビットの自動交換が可能であったり、集塵システム搭載、2.5Wダイオードレーザーも搭載とこれ一台でかなりのことが便利・お手軽にできるようになっています。工作精度もかなり高いようで、位置決め精度は0.01mm、作品サンプルもとても美しいです。あと、非公式ではあるものの開発者が鉄とかも削れるよ~って言ってます。

現在 Pre-Order 受付中で 20% 引きで購入することができるため、定価 $4999 のところを今なら $3999 で購入できます。この内容だと通常は$10000を下回ることは無い(というか、卓上型CNCでツールチェンジャーまで搭載した製品が無い)と思われるので、超お買い得と言えます。とは言え $3999 、日本円だと53万円、送料や各種オプション込みで65万円くらいになるのでちょっとハードルが高いです。Kickstarterで早いうちにプレッジしていれば $2550 で手に入れることができたんですが。残念無念。

ちなみにこれも中国の企業が作っています。中国の、製品を作り上げる力は本当に凄いです。

その他

その他にも卓上サイズのCNCフライスマシンはいろいろありますが、価格と内容で大体これまで挙げたものに落ち着くかなとおもいます。というか、Carveraのインパクトが凄すぎてこれが出る前に選択肢に上がっていたような同程度の価格帯の製品が一気に霞んでしまいました。凄すぎる。

そして日本製は残念ながらこのクラスでは選択肢は皆無です。工場で本格的に使うものなら色々あるんですが。

まとめ

いろいろ調べてわかったのは、低価格帯の製品を購入して育てていく前提でも何だかんだ全部で50~100万円くらいはかかるんじゃないかということでした。それだったら最初から Carvera を買ってその分作品作りに集中するのが結局良さそうに思えます。作品を作るのが目的であれば。そうじゃなくて、工作機械が好きなんだ!!特に作りたい作品なんかないんだ!!っていう人は自分で育てるのも楽しいかとおもいます。ただ、ベースになる製品の筋の良し悪しがそのまま育てやすさに繋がるので、そこまで見越すと omnio cnc や lycnc のように $2000~$3000 クラスの製品を選んだ方がいいでしょう。もしくは完全に割り切って数万円台のもっと小型の製品を選ぶのが良いかと思います。

結論としては、Carvera欲しいな~~~ということで。売り上げが立つ見込みがあるなら全然買ってもいいですけど、さすがにそういう見込みが無い状態でこれに突撃するのはなかなか躊躇われます。次点かつ現実的な路線としてはlycncの縦型が値段と内容のバランスが良くて魅力的です。ただ、結構筐体が大きいので置き場所を考える必要があります。あとは実際に動かしてみたらどうなのかというところですが、こればっかりは確認できないのが辛いところです。

電子工作

オペアンプ用に負電圧を作る

センサの値を扱う際、信号を増幅するのにオペアンプを使用します。そして、オペアンプを動作させるためには負電圧が必要になります。そのため、回路には+5Vとか+3.3Vという正の電圧しか供給されてない状態で負の電圧をどのようにして作るかというのが問題になります。

この時、チャージポンプICを使用することでIC一つとコンデンサ二個で簡単に負電圧を作ることができます。私が使ったのは秋月電子で購入できるTJ7660Nです。使い方はとても簡単で、データシートに載っているそのまま電源とGND、10μFのコンデンサ2つを繋げるだけです。

試しにブレッドボード上で回路を組んでみます。

きちんと値が出ているか確認します。まず、入力電源を測ります。

直流安定化電源で5Vを出力していますが、実測で4.98Vくらいが入力されています。

5番ピンとGNDの間の電圧を測ると-4.978Vくらいが出ていることが確認できます。十分な結果ですね。

あんまり大電流を出力することはできないですが、たったこれだけでマイナス電源が取れるのは配線の手間を考えるととても助かります。

VisionFive2

水位センサの値を取得する

VisionFive2 の I2C 経由で水位センサに接続できたので、値を取得してみます。i2c-tools の時もそうでしたが VisionFive2 上で Linux が動いているおかげで Raspberry Pi など Linux で動いている SBC とやることは同じなので既存のノウハウが流用できて助かります。

値の取得

センサの使い方は製造元のドキュメントを参照します。また、I2C との通信は”LinuxのC言語でI2Cデバイスと通信する“を参考にしています。VisionFive2 が Arduino IDE に対応していれば、製造元のドキュメントに書いてあるサンプルがほぼそのまま使えるかもしれないですが Arduino IDE への対応状況は未調査です。

最終的にできたプログラムは以下の通りです:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdint.h>

#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>


unsigned char low_data[8] = {0};
unsigned char high_data[12] = {0};

#define THRESHOLD      100

//i2c address
#define I2C_DEVICE         "/dev/i2c-0"
#define ATTINY1_HIGH_ADDR  0x78
#define ATTINY2_LOW_ADDR   0x77


int8_t i2c_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint16_t length) {
  int32_t fd = open(I2C_DEVICE, O_RDWR);
  if (fd == -1) {
    fprintf(stderr, "i2c_read: failed to open: %s\n", strerror(errno));
    return -1;
  }

  struct i2c_msg messages[] = {
      { dev_addr, 0, 1, &reg_addr },
      { dev_addr, I2C_M_RD, length, data },
  };
  struct i2c_rdwr_ioctl_data ioctl_data = { messages, 2 };

  if (ioctl(fd, I2C_RDWR, &ioctl_data) != 2) {
    fprintf(stderr, "i2c_read: failed to ioctl: %s\n", strerror(errno));
    close(fd);
    return -1;
  }

  close(fd);
  return 0;
}

void getHigh12SectionValue(void)
{
  memset(high_data, 0, sizeof(high_data));
  i2c_read(ATTINY1_HIGH_ADDR, 0, high_data, 12);
}

void getLow8SectionValue(void)
{
  memset(low_data, 0, sizeof(low_data));
  i2c_read(ATTINY2_LOW_ADDR, 0, low_data, 8);
}

int sense() {
  uint32_t touch_val = 0;
  uint8_t trig_section = 0;

  getHigh12SectionValue();
  getLow8SectionValue();

  printf("low_data :");
  for (int i = 0 ; i < 8; i++) {
    printf("%d ", low_data[i]);
    if (low_data[i] > THRESHOLD) {
      touch_val |= 1 << i;
    }
  }
  printf("\n");

  printf("high_data :");
  for (int i = 0 ; i < 12; i++) {
    printf("%d ", high_data[i]);
    if (high_data[i] > THRESHOLD) {
      touch_val |= (uint32_t)1 << (8 + i);
    }
  }

  printf("\n");

  printf("touch value= %x\n", touch_val);

  while (touch_val & 0x01) {
    trig_section++;
    touch_val >>= 1;
  }

  printf("water level = %d\n", trig_section * 5);
}

int main() {
  while(1) {
    sense();
    sleep(1);
  }

  return 0;
}

途中で気づいたんですが、Linux の機能を使うくらいなら Python からアクセスする方が楽かもしれないです。その場合、製造元が用意している grove.py にちょっと手を加えると使えそうな雰囲気でしたし、smbus2 モジュールを使用しても良いかもしれません。

動作確認

動作確認は、コップに入れた水の中でセンサを上下に動かしておこないます。

作成したプログラムをコンパイルして実行します。

chiyama@starfive:~$ make WaterLevel
chiyama@starfive:~$ sudo ./WaterLevel
sudo: unable to resolve host starfive: Name or service not known
low_data :249 249 249 249 250 249 249 249
high_data :248 248 248 248 246 0 0 0 0 0 0 0
touch value= 1fff
water level = 65
low_data :249 249 249 249 249 249 249 249
high_data :248 248 248 248 247 0 0 0 0 0 0 0
touch value= 1fff
water level = 65
low_data :249 249 249 249 249 249 249 249
high_data :248 248 248 248 240 0 0 0 0 0 0 0
touch value= 1fff
water level = 65
low_data :249 249 249 249 180 150 90 31
high_data :248 247 247 187 0 0 0 0 0 0 0 0
touch value= f3f
water level = 30
low_data :249 249 2 255 0 0 0 0
high_data :0 0 0 0 0 0 0 0 0 0 0 0
touch value= b
water level = 10
low_data :249 249 0 0 0 0 0 0
high_data :0 0 0 0 0 0 0 0 0 0 0 0
touch value= 3
water level = 10
low_data :249 249 249 249 249 249 249 249
high_data :0 0 0 0 0 0 0 0 0 0 0 0
touch value= ff
water level = 40
low_data :249 249 249 249 249 249 249 250
high_data :248 248 0 0 0 0 0 0 0 0 0 0
touch value= 3ff
water level = 50
low_data :249 249 249 249 249 249 249 249
high_data :248 248 248 248 0 0 0 0 0 0 0 0
touch value= fff
water level = 60
low_data :249 249 249 249 215 204 255 249
high_data :245 251 251 0 0 0 0 0 0 0 0 0
touch value= 7ff
water level = 55
low_data :225 0 0 0 0 0 0 0
high_data :0 0 0 0 0 0 0 0 0 0 0 0
touch value= 1
water level = 5
low_data :249 0 0 0 0 0 0 0
high_data :0 0 0 0 0 0 0 0 0 0 0 0
touch value= 1
water level = 5
low_data :249 249 249 249 249 249 249 249
high_data :248 248 248 247 0 0 0 0 0 0 0 0
touch value= fff
water level = 60
^C
chiyama@starfive:~$

センサを上下すると water level の値が変化しています。出ている値もセンサ上の水位と合致しているようです。

VisionFive2

StarFive VisionFive2 で I2C を使う

センサや A/D, D/A などは I2C 接続して使用すると市販のものがそのまま使えて便利なので、今回は VisionFive2 で I2C を使ってみます。

接続

VisionFive2 にも RaspberryPi のように GPIO ヘッダがあり、I2C 接続もできるようになっているのでドキュメントの通りに接続します。

今回はスイッチサイエンスで入手できる Grove 用水位センサを接続します。

シリアルやオシロスコープのプローブもあるのでちょっとゴチャゴチャしていますが、Grove 用コネクタからそのまま VisionFive2 のピンに繋げているだけです。

dtsファイルの編集

I2c 接続を使用できるように dts ファイルを設定します。dts ファイルは VisionFile SDK の /linux/arch/riscv/boot/dts/starfive/jh7110-visionfive-v2.dtsi です。今回は 3 番(GPIO58)と 5 番(GPIO57)ピンを使うので、デフォルトのまま i2c-0 として使用可能です。

I2C 情報の確認

はじめに i2c-tools をインストールします

chiyama@starfive:~$ sudo apt install i2c-tools

i2cdetect が使用可能になるので、I2C バスの情報を確認してみます。

chiyama@starfive:~$ sudo i2cdetect -l
sudo: unable to resolve host starfive: Name or service not known
i2c-0 i2c Synopsys DesignWare I2C adapter I2C adapter
i2c-2 i2c Synopsys DesignWare I2C adapter I2C adapter
i2c-5 i2c Synopsys DesignWare I2C adapter I2C adapter
i2c-6 i2c Synopsys DesignWare I2C adapter I2C adapter
i2c-7 i2c Inno HDMI I2C adapter
chiyama@starfive:~$

i2c-0 が認識されているので、続いてデバイスの確認をします。

chiyama@starfive:~$ sudo i2cdetect -y -r 0
sudo: unable to resolve host starfive: Name or service not known
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77
chiyama@starfive:~$

製品ページによると I2C アドレスは 0x77 と 0x78 を使用しているとのことで、0x77 は認識できています。しかし、0x78 以上は I2C では 10 ビットアドレスモードでないと認識できないようです。man を見ても i2cdetect では 10 ビットアドレスモードに言及されていないので、対応していないのかもしれません。ひとまず 0x77 が認識できたので良しとしておきます。

データを取得する

i2cget でデータを取得してみます。

chiyama@starfive:~$ sudo i2cget 0 0x77
sudo: unable to resolve host starfive: Name or service not known
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will read from device file /dev/i2c-0, chip address 0x77, current data
address, using read byte.
Continue? [Y/n] y
0x00
chiyama@starfive:~$

このとき、オシロスコープの波形もみておきます。

それっぽい波形が出ているので、ちゃんと動いているようですね。ここまでくれば、プログラムからセンサデータの読み書きはできそうです。

VisionFive2

StarFive VisionFive2 で X を立ち上げる

StarFive VisionFive2 着弾からの続きです。公式から配布されている Image-69 を使うためには SPL と U-Boot を更新しなければいけないので、そのために VisionFive2 SDK から取得できる sdcard.img を使って Linux を立ち上げるところまでできました。

SPL と U-Boot の更新

sdcard.img がビルドできているなら、同じ場所に u-boot-spl.bin.normal.out と visionfive2_fw_payload.img ができているので、VisionFive2 に転送します。

※ここでネットワークが繋がっている必要があるので手持ちの USB Wifi ドングルを繋げてみたところ、そのままではネットワークインターフェースとして認識してくれませんでした。調べるのがめんどくさいので私は有線接続してしまいましたが、USB Wifi をサクッと動かしたい人はボードと一緒に公式から購入してしまうのが良いかもしれないです。

sdcard.img で立ち上げた環境では root での ssh 接続が許可されているので、scp を使用して転送できます。

chiyama@riscv:~/VisionFive2/VisionFive2/work$ scp u-boot-spl.bin.normal.out root@192.168.0.111:.
root@192.168.0.111's password:
u-boot-spl.bin.normal.out 100% 125KB 915.4KB/s 00:00
chiyama@riscv:~/VisionFive2/VisionFive2/work$ scp visionfive2_fw_payload.img root@192.168.0.111:.
root@192.168.0.111's password:
visionfive2_fw_payload.img 100% 2728KB 4.8MB/s 00:00
chiyama@riscv:~/VisionFive2/VisionFive2/work$

続いて Updating SPL and U-Boot の手順通りに SPL と U-Boot を更新します。

root@starfive:~# flashcp -v u-boot-spl.bin.normal.out /dev/mtd0
root@starfive:~# flashcp -v visionfive2_fw_payload.img  /dev/mtd1

X の起動

再起動して無事に立ち上がることが確認できたら SD カードに Image-69 を焼いてそれで立ち上げます。。。が、シリアル上では OS が立ち上がっているのに HDMI 側に X が表示されない。何でかー???と思たら、キーボードやマウスが繋がっていないと X が立ち上がらないようです。これ以外にも X 周りはまだ不安定なようで、突然死んだり色々起こるようです。

動作確認

gcc と make を入れてプログラムをコンパイルしてみます。

root@starfive:~# apt install gcc
root@starfive:~# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/riscv64-linux-gnu/11/lto-wrapper
Target: riscv64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 11.3.0-3' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --program-prefix=riscv64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libitm --disable-libquadmath --disable-libquadmath-support --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --disable-multilib --with-arch=rv64gc --with-abi=lp64d --enable-checking=release --build=riscv64-linux-gnu --host=riscv64-linux-gnu --target=riscv64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.3.0 (Debian 11.3.0-3)
root@starfive:~# apt install make
root@starfive:~# cat hello.c
#include <stdio.h>

int main(int argc, char **argv) {
  printf("Hello, StarFive2!!\n");

  return 0;
}

root@starfive:~# make hello
cc     hello.c   -o hello
root@starfive:~# ./hello
Hello, StarFive2!!
root@starfive:~#

いい感じですねっ!!これで RISC-V を手軽に試すことのできる環境が整いました。これまでは”作って学ぶコンピュータアーキテクチャ“を読むときにはシミュレータを使ってきたのですが、動作がかなり重いので実機があるのはとても嬉しいです。

処理能力について

普段、Raspberry Pi4 を使う時には GUI を使わないのであまり正確な比較はできないものの、X の立ち上がり時間なんかは RPi4 に比べて結構かかるかな?という気がします。コンソールで処理したり、hello をコンパイル・実行するくらいなら当然ですが全くストレスは感じないです。このあたりはベンチマークも取ってみたいですね。

VisionFive2

StarFive VisionFive2 着弾

StarFive が夏に Kickstarter でクラウドファンディングしていた VisionFile2 が年末に突然届きました。

RISC-V は組み込み用途としては既にかなりの実績があるものの、計算能力を高めることを目指したものはあまりなく、物足りなさを感じていたところでした。

そこに現れたのが StarFive JH7110 を搭載した VisionFive2 です。Quad core 1.5GHz の CPU で GPU も搭載していて、Raspberry pi4 よりもちょっと落ちるもののそれに匹敵する性能と GPIO や I2C といった各種インターフェース、DDR4, PCIe, M.2 NVMe SSD 対応と色々遊べそうな構成になっています。また、Linux が動いて手軽に実環境でプログラムを実行することができるのも嬉しいです。

内容物

届いた中身はとてもシンプルです。外箱とプラスチックケースに収められた VisionFive2 が入っているだけです。ドキュメント類は外箱の裏に書かれた URL からアクセスします。

資料

資料は外箱裏に載っている

の二か所を参照します。メーカーが提供しているイメージのダウンロード URL は Getting Started の Flasing with Linux or Windows のページにあるのが最初ちょっと不安感を煽ります。笑。確かに、必要な資料を読み進めると必要な時に出てくるので良いのですが。

あと、2022年12 月末時点では VisionFive2 を動かすにはいくつかハマり所があり、それを @osakanataro2 さんが懇切丁寧に整理されているので、この記事も参考にします。

StarFive Vision Five 2の2022年12月出荷分を使うには面倒くさい手順が必要

ちなみに、最近の中国製のこういうプロダクトは”翻訳すれば読めるでしょ?”という感じで中国語で書かれていることが結構あり、実際に翻訳されたものを読むだけで十分なので突然中国語が出てきても Google 様の力で翻訳してもらって読み進めます。VisionFive2 はかなりの部分が英語で書かれていますが、フォーラムなんかは中国語が多いですね。

作業

基本は前述の @osakanataro2 さんの記事に全て書いてあります。ただ、sdcard.img の作り方だけはサラッと流されていたので、補足しておきます。→元記事では releases にあるものを使う想定だったようです。たしかにこちらを使えばわざわざビルドする必要無いです。

sdcard.img を作るのは starfive-tech/VisionFive2 に書いてる手順に沿って行えば良いです。ただ、私の環境では開発者用環境のチェックアウトまで行わないとビルドが通らなかったので、その作業も行いました。

$ cd buildroot && git checkout --track origin/JH7110_VisionFive2_devel && cd ..
$ cd u-boot && git checkout --track origin/JH7110_VisionFive2_devel && cd ..
$ cd linux && git checkout --track origin/JH7110_VisionFive2_devel && cd ..
$ cd opensbi && git checkout master && cd ..
$ cd soft_3rdpart && git checkout JH7110_VisionFive2_devel && cd ..

ビルドの際、リポジトリには pre-build.sh, build.sh, post-build.sh があってこれを叩けばいいのではないかと思ってしまうのですが、ここはドキュメント通り make を実行するだけで 良いようです。ビルドは結構時間がかかり、私はビルドをしていたら年が明けてしまいました。

ビルドができたら Additional command to config buildroot, uboot, linux, busybox の項目を実行し、最後に genimage.sh を実行すると work/sdcard.img が作られます。このあたりの手順は刻々と状況が変わっていくと思うので、その時々試行錯誤することになるかと思います。

sdcard.img ができたら SDカードにイメージを焼いて VisionFive2 を起動すると Linux が立ち上がります。

U-Boot SPL 2021.10 (Oct 31 2022 - 12:11:37 +0800)
DDR version: dc2e84f0.
Trying to boot from SPI

OpenSBI v1.0
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name             : StarFive VisionFive V2
Platform Features         : medeleg
Platform HART Count       : 5
Platform IPI Device       : aclint-mswi
Platform Timer Device     : aclint-mtimer @ 4000000Hz
Platform Console Device   : uart8250
Platform HSM Device       : ---
Platform Reboot Device    : ---
Platform Shutdown Device  : ---
Firmware Base             : 0x40000000
Firmware Size             : 360 KB
Runtime SBI Version       : 0.3

(中略)

Starting NFS statd: OK
Starting NFS services: OK
Starting NFS daemon: rpc.nfsd: Unable to access /proc/fs/nfsd errno 2 (No such file or directory).
Please try, as root, 'mount -t nfsd nfsd /proc/fs/nfsd' and then restart rpc.nfsd to correct the problem
FAIL
Starting NFS mountd: rpc.mountd: svc_tli_create: could not open connection for udp6
rpc.mountd: svc_tli_create: could not open connection for tcp6
rpc.mountd: svc_tli_create: could not open connection for udp6
rpc.mountd: svc_tli_create: could not open connection for tcp6
rpc.mountd: svc_tli_create: could not open connection for udp6
rpc.mountd: svc_tli_create: could not open connection for tcp6
OK
Starting isp_ctrl_daemon.sh: OK
Starting DHCP server: FAIL

Welcome to Buildroot
buildroot login: [   38.976567] mipi_0p9: disabling
root
Password: [  174.736301] random: crng init done
[  174.739759] random: 7 urandom warning(s) missed due to ratelimiting

# uname -a
Linux buildroot 5.15.0 #1 SMP Sat Dec 31 23:18:30 JST 2022 riscv64 GNU/Linux
#

sdcard.img を使った Linux 環境が立ち上がったので、あとは @osakanataro2 さんの記事に沿って作業を進めればよいです。

ちょっと長くなったので今回はここまで。