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, ®_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 の値が変化しています。出ている値もセンサ上の水位と合致しているようです。