弊社、離島なんかに太陽光パネルで自律動作する農業用モニタリングポストなんかを設置しているんですが、中に使っているチャージコントローラから電圧や電流なんかをモニタリングしたいんですよ。
http://www.vill.minamidaito.okinawa.jp/pdf/youran-pdf/14_industry.pdf
お、件の農業用ポストもチラ見できる、プロジェクトのYoutubeチャンネルなんてあるんだ。
で、最近のそういうのはWindowsやスマホからデータを取得するアプリがついているんですが、そういうアプリでは不便なので、modbus経由で取得できるようになっています。(できた順序はたぶん逆なんだけど、一般ユーザはmodbusなんかは触らんのでな)

で、今使ってるEPEEVERのTracer 2610っていうやつ生えてるmodbusポート用の接続ケーブルを買ってあったんですが、
https://www.epever.com/product/rs485-4llt-accessories-communication-cable/

Windowsからはデータが取得できるんですが、モニタリングポストに入ってるのはラズパイなので、そのままでは使用できないんですよね。
(いま気づいたんだけど、このWindowsアプリ .netで書かれてるので、実はmonoで動いたりしないかなw)
というわけで、このケーブルを使って生でmodbusを触ってデータを取ろうとしたんですが、全然取れない。
EPEVERのチャージコントローラは多数あって、いずれも同じプロトコルで取れるはずで、実際に汎用の485トランシーバーを使って取得している例は多数あります
ほかのTracerのモデルだと、modbusポートが端子台だったり、RJ-45だったりするので、そういうことをやる人は大体直接ケーブルを突っ込んで、自分のトランシーバでやってるんですよね。

https://community.blynk.cc/t/epsolar-tracer-2210a-charge-controller-blynk-epic-solar-monitor/10596
でも、なぜかいま手元にあるモデルは、変な4ピンの防水ケーブルがついてて、それとUSBを変換する純正のケーブルなので、これを加工するのは一寸めんどくさい。なので、できればこのケーブルのまま取りたかったんですよ。
このUSBケーブルの中に入っているチップは、XR21V1414というやつで、RasPiの標準ドライバに入ってたり入ってなかったりみたいで、どうもドライバをビルドしたりしないといけなかったり、はたまた標準のUSB-CDCドライバと喧嘩したりして、なんか動作が安定しませんw
どうにかドライバが入ったので、pyserialやpymodbusを使って、modbusデータの送受信を試しますが、どうしても応答が帰ってこない。
modbusで使われるRS-485は、送信時にデータイネーブルするとバスをつかみ、データ送信が終わったら、バスを開放してデータを待つ、という半二重プロトコルです。

近年のパソコンのI/Fで、こういうタイミング重視の通信を直接することはないので、USBからの変換の場合、RTSを使ったハンドシェイクでバス開放を行うか、データの送信バッファがない状態ではタイミングを見計らって自動でバスを解放してくれるような仕組みになっているものが多いです。
別の485受信機やオシロスコープををパラレルにつなぎ、信号をモニタリングしてみたんですが、どうやらマスタからコマンドを送った後、バスの開放が全然起きません。
これが原因だと考えてあちこち調べてみたところ、XR21V1414もどうやらRTSによる自動開放を行ってくれる、というところまではたどり着きました。Windowsのアプリでは、これを利用してmodbusコマンドがきちんと送受信されている模様。
では、Linuxでもうまくいくはず、と同じようにやってみたつもりなんですが、最初にCursorに書かせたpyserialとpymodbusを使ったプログラムがどうしてもちゃんと動かない。バスが解放されない。
なんでや、と探したところ、同じようにpythonで書かれた、まさに同じことをしているライブラリを見つけたので動かしてみたところ、やっぱり動かないw
「純正ケーブルはWindowsでならちゃっちゃと動くけどRasPiはめんどいよ。カスタムでケーブル作れよ」なんて書いてあります。おお、やっぱりそこでハマってるんだ。
同じコードでWindows、しかもPython上ではさっと動く、のにLinuxでは動かないらしい。これは今のところ仮説と誰かがどっかでつぶやいていたんですが、pyserialとpymodbusでRTSを使って送受信を切り替えようとすると、それがめっちゃ遅い、可能性がある、というところのようです。ハードウェアハンドシェイクにしたらそんなことはないだろ、と思ってちょっと試してみたんですが(やり方があってるのかはわからんのですが)やっぱり動かない。マジかよ。しゃぁないやってみるか、と生のCを使って例によってハードウェアRTSを指定してmodbusコマンドを投げたところ...サクッと動いた。。。まじか。
int configure_serial_port(int fd, int baudrate) {struct termios tty;if (tcgetattr(fd, &tty) != 0) {perror("tcgetattr");return -1;}// ボーレート設定cfsetospeed(&tty, baudrate);cfsetispeed(&tty, baudrate);// 8N1設定tty.c_cflag &= ~CSTOPB; // 1ストップビットtty.c_cflag &= ~CSIZE; // データビットサイズをクリアtty.c_cflag |= CS8; // 8ビットtty.c_cflag |= CREAD | CLOCAL; // 受信有効、モデム制御無効// ローカルモードtty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);// 生入力モードtty.c_iflag &= ~(IXON | IXOFF | IXANY);tty.c_iflag &= ~(ICRNL | INLCR | IGNCR);// 生出力モードtty.c_oflag &= ~OPOST;// タイムアウト設定tty.c_cc[VMIN] = 0; // 最小文字数if (tcsetattr(fd, TCSANOW, &tty) != 0) {perror("tcsetattr");return -1;}return 0;}
もしかすると、XR21V1414のRTSは、汎用GPIOを使って制御しているらしいので、ここの実装がタコなのかも???

というわけで、pyserialとpymodbusを使って、得体の知れない、メジャーではないRS-485変換器を使ってmodbusデータをRasPiで取ろうとすると、変にハマることがある、というお知らせでした。
んんーーーーー、これだけやって、純正ケーブルで取れるようにしたが、これはダメな気がする!!ケーブルぶった切ってもうちょっとましな485トランシーバ、もしくはGPIOから直接変換したほうがましな気がするぞ!!!
っていうか、このポートに挿せるBLEやWiFiの変換器が出てるらしいので、たぶんこっちを使ってほしいんだろうな。。