こんにちは、楠川です。
お正月休みは何をして過ごしましたか?
私はCyberGearと共に過ごしたお正月となりました。
CyberGearは何かというと、Xiaomi社が販売している低価格のモーターで、速度が比較的遅い代わりに強いトルクで位置決めできるのが魅力のモータです。
そんな魅力に惹かれて個人で買う人も続出中(?)。
会社内でも購入している人がちらほらいて、2024/1/9には、中国通販大手のAliExpressでは品切れ中との噂もありました。
X内で行われた「#シャオミさん頼むからこの製品日本に投入してください選手権」では、一部の人たちに話題になっていたのが記憶に新しい。
これ個人で買って何に使うのですか。
— Xiaomi Japan (@XiaomiJapan) December 26, 2023
私は、そんなCyberGearを個人で買うことで、お正月休みに楽しくモータと過ごすことができました。
ここからは、どんなふうにモータと過ごしたのか語らせていただきます。
はじめてのCAN通信
CyberGearはCAN通信で動きます。
恥ずかしながら、私は今までCAN通信をした経験がありませんでした。
そんな私はCyberGearをきっかけに、手持ちのRaspberry Piで、はじめてのCAN通信に挑戦しました。
Raspberry Piはシングルボードコンピュータ。
小さなコンピュータにパワフルなモーターがつくと思うと、どこかワクワクしてしまいます。
そんなワクワクを胸のうちに秘めながら、Raspberry Pi Zero WHにWaveshare社のRS485 CAN HAT for Raspberry PiをつけてCAN通信することにしました。
ラズパイは、ケースがなければ始まらない
Raspberry PiにRS485 CAN HATをつけてみました。
ピッタリ合うように設計された基板は美しい。いい感じです。
こんないい感じの基板、ケースに入れないと勿体ない。
そう思い、ケースを作ることにしました。
ネットのケースデータを改造しよう
今や、ネットでいろいろ手に入る時代。
似たようなHAT用のケースをアップしてくれている方がいました。
このケースデータを元に、ピッタリ合うケースを作りたい。
そう思って、個人用のFusion360でデータをいじらせてもらいました。
不要な穴を塞いで、必要な新しい穴をあけ、自宅のプリンターで印刷したケースがこちらです。
似たようなデータがアップロードされているとケースが簡単に作れて助かります。
CAN通信の設定をしよう
RS485 CAN HATはRaspberry Piの設定をしないと使えません。
CAN通信用のRaspberry Piの設定はPragmatic Linuxのページを参考に設定。
設定が終わった後でモーターと繋いだ後に24V電源を接続したら、CAN通信でのコミュニケーションが確認できました。
いろいろなサイトを参考にしながら格闘した結果、Raspberry PiのCAN設定は記事最後の設定方法に落ち着きました。
直感的に動かせるようにしていきたい
CANコマンドを手で打っていけばモーターを動かせるのですが、手間がかかるし直感的ではありません。
プログラミングが得意な人は、コマンドをぱぱぱっと出せるのでしょう。
ですが、私はプログラミングは不得意です。積極的にプログラムを触りたいと思いません。
そんな私はプログラムをあまり書かなくて済むNode-REDで動かせるようにトライしました。
Node-REDで直感的にCAN通信するために、Node-REDの下準備(記事の最後に記載)をした後でプログラミングをしていきます。
プログラミングは不得意なので、基本的なこともChatGPTに聞きながらプログラミングをしていきます。
ChatGPTなどの生成AIには間違いもありますが、自分でプログラムを書くよりも間違いは少ないので助かります。
・・・と、そんな感じでモゾモゾしていたら、お正月休みが終わりました。
専門外だからこそ、教わることが多くて楽しい
普段私は回路設計をしているので、3D CADやLinuxの設定やプログラミングと縁遠い。
そのため遅々として進みません。
ですが、一人で作業をしていたはずなのに、ケースの3Dデータを使わせてもらったり、Linuxの設定を教えてもらったり、ノードを使わせてもらったり、ChatGPTにコードを聞いたりと、ネットに助けられながら前に進むことができました。
CyberGearと共に過ごしたことで、ネットの中の人やAIに教わることができ、CAN通信など知らないことが知れて、楽しいお正月休みとなりました。
個人でCyberGearを買うと、こんな楽しく過ごせるんですね。
力こそパワー
スーパーカーは公道でたいして役に立ちません。「個人で買って何に使うのですか?」と言われることもあるでしょう。
でも、スーパーカーの所有者は、不必要なまでに鍛え上げられたトルクと速度を持った車にロマンを感じ、それを自分が所有することに悦びを感じているのではないでしょうか。
強トルクというロマンを秘めたCyberGear。所有している人はスーパーカー所有者と同じような悦びを感じているものと思います。
(本記事はすべて個人の感想です)
設定などなど
Raspberry PiのCAN設定
Raspberry Pi OS上で実行したコマンド
#SPIの有効化とHATの有効化。必要に応じて/boot/config.txtに設定を追記してください。 #手持ちのHATは12MHz品だったためoscillator=12000000とした echo 'dtparam=spi=on' | sudo tee -a /boot/config.txt echo 'dtoverlay=mcp2515-can0,oscillator=12000000,interrupt=25,spimaxfrequency=2000000' | sudo tee -a /boot/config.txt #再起動 sudo reboot -n #必要なCAN用カーネルモジュールをロード sudo modprobe can sudo modprobe can_raw #CANポートをCyberGearのボーレート1Mbpsで有効化 sudo ip link set can0 type can bitrate 1000000 restart-ms 100 sudo ip link set up can0 #CANユーティリティをインストール sudo apt install can-utils
Node-REDの下準備
Node-REDをインストールして起動したのち、下記二つのノードをパレットに追加しました。
https://flows.nodered.org/node/node-red-contrib-socketcan
https://flows.nodered.org/node/node-red-dashboard
ノードの追加方法は、下記がわかりやすかったです。
https://qiita.com/takeyan/items/fff5e8d64da25220a0c0#1-node-redにnode-red-dashboardを組み込む
Node-RED Flow JSON
注意:動いた!というだけのコードで、仕様の勘違いなどが多分に含まれている可能性があります。
非常に強いトルクを持つモーターが暴走する可能性がありますので、ご使用の際は細心の注意を払いながらご使用ください。
[{"id":"829af3ee.a57c1","type":"tab","label":"Flow 1","disabled":false,"info":""},{"id":"73d8023122f93a79","type":"ui_group","name":"送信データ","tab":"32a61c4be6454a78","order":1,"disp":true,"width":"7","collapse":false,"className":""},{"id":"c4ed8d183dc6b2b8","type":"socketcan-config","interface":"can0"},{"id":"aed2c45b95c6f503","type":"ui_group","name":"受信データ","tab":"32a61c4be6454a78","order":2,"disp":true,"width":"7","collapse":false,"className":""},{"id":"32a61c4be6454a78","type":"ui_tab","name":"ホーム","icon":"dashboard","disabled":false,"hidden":false},{"id":"92a2c46d4df731c7","type":"ui_base","theme":{"name":"theme-light","lightTheme":{"default":"#0094CE","baseColor":"#0094CE","baseFont":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif","edited":false},"darkTheme":{"default":"#097479","baseColor":"#097479","baseFont":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif","edited":false},"customTheme":{"name":"Untitled Theme 1","default":"#4B7930","baseColor":"#4B7930","baseFont":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"},"themeState":{"base-color":{"default":"#0094CE","value":"#0094CE","edited":false},"page-titlebar-backgroundColor":{"value":"#0094CE","edited":false},"page-backgroundColor":{"value":"#fafafa","edited":false},"page-sidebar-backgroundColor":{"value":"#ffffff","edited":false},"group-textColor":{"value":"#1bbfff","edited":false},"group-borderColor":{"value":"#ffffff","edited":false},"group-backgroundColor":{"value":"#ffffff","edited":false},"widget-textColor":{"value":"#111111","edited":false},"widget-backgroundColor":{"value":"#0094ce","edited":false},"widget-borderColor":{"value":"#ffffff","edited":false},"base-font":{"value":"-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif"}},"angularTheme":{"primary":"indigo","accents":"blue","warn":"red","background":"grey","palette":"light"}},"site":{"name":"Node-RED ダッシュボード","hideToolbar":"false","allowSwipe":"false","lockMenu":"false","allowTempTheme":"true","dateFormat":"YYYY/MM/DD","sizes":{"sx":48,"sy":48,"gx":6,"gy":6,"cx":6,"cy":6,"px":0,"py":0}}},{"id":"a3ae413fd45f4857","type":"delay","z":"829af3ee.a57c1","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":650,"y":140,"wires":[["bcd2dc2348c39389"]]},{"id":"e0b24fba6b4fc5f9","type":"ui_button","z":"829af3ee.a57c1","name":"","group":"73d8023122f93a79","order":1,"width":0,"height":0,"passthru":false,"label":"初期設定","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"1200007f#0570000001000000","payloadType":"str","topic":"topic","topicType":"msg","x":140,"y":100,"wires":[["4b3e2f3064e4fabf"]]},{"id":"4b3e2f3064e4fabf","type":"change","z":"829af3ee.a57c1","name":"モーター停止命令","rules":[{"t":"set","p":"payload","pt":"msg","to":"0400007f#0000000000000000","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":390,"y":100,"wires":[["a3ae413fd45f4857","358b375f8bcf147d"]]},{"id":"bcd2dc2348c39389","type":"change","z":"829af3ee.a57c1","name":"位置制御モードへ移行","rules":[{"t":"set","p":"payload","pt":"msg","to":"1200007f#0570000001000000","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":400,"y":220,"wires":[["84675f8ea94b1c38","99c8f6706859148f"]]},{"id":"84675f8ea94b1c38","type":"delay","z":"829af3ee.a57c1","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":650,"y":260,"wires":[["2810280918ec0543"]]},{"id":"358b375f8bcf147d","type":"link out","z":"829af3ee.a57c1","name":"CANデータ送信へ","mode":"link","links":["d37a21bf35e2c904","02d9ce1d8563dbb1"],"x":635,"y":100,"wires":[]},{"id":"99c8f6706859148f","type":"link out","z":"829af3ee.a57c1","name":"CANデータ送信へ","mode":"link","links":["d37a21bf35e2c904","02d9ce1d8563dbb1"],"x":635,"y":220,"wires":[]},{"id":"2810280918ec0543","type":"change","z":"829af3ee.a57c1","name":"原点復帰","rules":[{"t":"set","p":"payload","pt":"msg","to":"0600007f#0100000000000000","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":340,"wires":[["9f6e322718b1abe1","a123812119064775"]]},{"id":"5435ed4a4363e2f8","type":"change","z":"829af3ee.a57c1","name":"モーターイネーブル命令","rules":[{"t":"set","p":"payload","pt":"msg","to":"0300007f#0000000000000000","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":600,"wires":[["05eef2189d4ae35f"]]},{"id":"a123812119064775","type":"delay","z":"829af3ee.a57c1","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":650,"y":400,"wires":[["60cd64df26bfe89e"]]},{"id":"9f6e322718b1abe1","type":"link out","z":"829af3ee.a57c1","name":"CANデータ送信へ","mode":"link","links":["d37a21bf35e2c904","02d9ce1d8563dbb1"],"x":635,"y":340,"wires":[]},{"id":"05eef2189d4ae35f","type":"link out","z":"829af3ee.a57c1","name":"CANデータ送信へ","mode":"link","links":["d37a21bf35e2c904","02d9ce1d8563dbb1"],"x":635,"y":600,"wires":[]},{"id":"d13761437b76cc37","type":"ui_slider","z":"829af3ee.a57c1","name":"","label":"モーター位置","tooltip":"","group":"73d8023122f93a79","order":2,"width":0,"height":0,"passthru":true,"outs":"all","topic":"topic","topicType":"msg","min":"16128","max":"16585","step":1,"className":"","x":360,"y":740,"wires":[["55041e4dcb8338f1"]]},{"id":"ac54ca41928c695e","type":"function","z":"829af3ee.a57c1","name":"CAN用位置データ生成","func":"// 入力データを取得\nvar originalPayload = msg.payload;\n\n// 位置モードの角度指令0x7016をモーターに書き込み\n//「1200007f#167000000000」を先頭に追加\n//(本来なら4byteだが上位2byteデータとした)\nvar modifiedPayload = \"1200007f#167000000000\" + originalPayload;\n\n// 変換されたデータを出力\nmsg.payload = modifiedPayload;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":560,"y":980,"wires":[["e11de85eb331562d"]]},{"id":"74646c8e365330b3","type":"function","z":"829af3ee.a57c1","name":"入力値を2バイトの16進数に変換","func":"// 入力値を取得\nvar inputValue = msg.payload; // 入力値として0から65535までの数値が入ってくるものと仮定します\n\n// 整数を16進数の2バイトに変換\nvar hexValue = inputValue.toString(16);\n\n// 2バイトになるように左側に0を追加(必要であれば)\nwhile (hexValue.length < 4) {\n hexValue = '0' + hexValue;\n}\n\n// 変換された16進数の2バイトを出力\nmsg.payload = hexValue;\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":600,"y":900,"wires":[["ac54ca41928c695e"]]},{"id":"4c1baae913f4d870","type":"inject","z":"829af3ee.a57c1","name":"起動時初期化","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"0","payloadType":"num","x":180,"y":740,"wires":[["d13761437b76cc37"]]},{"id":"e11de85eb331562d","type":"link out","z":"829af3ee.a57c1","name":"CANデータ送信へ","mode":"link","links":["d37a21bf35e2c904","02d9ce1d8563dbb1"],"x":825,"y":980,"wires":[]},{"id":"6013eea94efbf3b5","type":"comment","z":"829af3ee.a57c1","name":"Dashboard処理","info":"","x":120,"y":60,"wires":[]},{"id":"5f671b29.cd24bc","type":"socketcan-out","z":"829af3ee.a57c1","name":"socketcan-out","config":"c4ed8d183dc6b2b8","x":150,"y":1360,"wires":[["103b34b1e043488a","5a61cf62a8b994c2"]]},{"id":"912f9928.da2758","type":"comment","z":"829af3ee.a57c1","name":"Receiove CAN data from interface CAN0","info":"","x":200,"y":1300,"wires":[]},{"id":"103b34b1e043488a","type":"function","z":"829af3ee.a57c1","name":"ID-dec2hex","func":"// 入力データを取得\nvar decimalNumber = msg.payload.canid; // ここでmsg.payloadは入力された10進数データです\n\n// 10進数から16進数に変換\nvar hexadecimalNumber = decimalNumber.toString(16);\n\n// 変換されたデータを出力\nmsg.payload.canid = hexadecimalNumber;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":1360,"wires":[["bdd602de7910bb1f"]]},{"id":"bdd602de7910bb1f","type":"function","z":"829af3ee.a57c1","name":"ID-slise6","func":"// 入力データを取得\nvar canidslise6 = msg.payload.canid; // msg.payload.canidが文字列と仮定しています\n\n// 下6桁の文字を削除\nvar modifiedCanid = canidslise6.slice(0, -6);\n\n// データがからの場合は0を出力\nif (!modifiedCanid || modifiedCanid.trim() === '') {\n modifiedCanid = 0;\n}\n\n// 変更されたデータを出力\nmsg.payload.canidslise6 = modifiedCanid;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":500,"y":1360,"wires":[["49accc84315388fc"]]},{"id":"49accc84315388fc","type":"switch","z":"829af3ee.a57c1","name":"通信タイプ判別","property":"payload.canidslise6","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"str"},{"t":"eq","v":"1","vt":"str"},{"t":"eq","v":"2","vt":"str"},{"t":"eq","v":"3","vt":"str"},{"t":"eq","v":"4","vt":"str"},{"t":"eq","v":"6","vt":"str"},{"t":"eq","v":"7","vt":"str"},{"t":"eq","v":"17","vt":"str"},{"t":"eq","v":"18","vt":"str"},{"t":"eq","v":"21","vt":"str"},{"t":"eq","v":"22","vt":"str"},{"t":"eq","v":"12","vt":"str"}],"checkall":"true","repair":false,"outputs":12,"x":700,"y":1460,"wires":[[],[],["e3b1052f578cf301"],[],[],[],[],[],[],[],[],[]]},{"id":"e3b1052f578cf301","type":"function","z":"829af3ee.a57c1","name":"8個の配列を4つにまとめる","func":"// 入力データを取得\nvar hexArray = msg.payload.data; // ここでmsg.payloadは8個の16進数の配列データと仮定します\n\n// 2つずつの16進数データをまとめて新しい配列に追加\nvar combinedArray = [];\nfor (var i = 0; i < hexArray.length; i += 2) {\n var combinedHex = hexArray[i]*16*16 + hexArray[i + 1];\n combinedArray.push(combinedHex);\n}\n\n// 変換されたデータを出力\nmsg.payload.data = combinedArray;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":260,"y":1620,"wires":[["e323e8f8d46a902a","e8c02e6afe8afc76","7bc07fcdee82ca57","a5bfc492512d3b3f"]]},{"id":"18f9a3a97272bb94","type":"ui_chart","z":"829af3ee.a57c1","name":"","group":"aed2c45b95c6f503","order":3,"width":"6","height":"4","label":"現在角度","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":860,"y":1620,"wires":[[]]},{"id":"e323e8f8d46a902a","type":"change","z":"829af3ee.a57c1","name":"data[0]抽出","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.data[0]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":530,"y":1620,"wires":[["143eb716e4ca74ec"]]},{"id":"e8c02e6afe8afc76","type":"change","z":"829af3ee.a57c1","name":"data[1]抽出","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.data[1]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":530,"y":1660,"wires":[["cd11f08432d136ac"]]},{"id":"7bc07fcdee82ca57","type":"change","z":"829af3ee.a57c1","name":"data[2]抽出","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.data[2]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":530,"y":1700,"wires":[["c36bff986c6b9ae3"]]},{"id":"a5bfc492512d3b3f","type":"change","z":"829af3ee.a57c1","name":"data[3]抽出","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.data[3]","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":530,"y":1740,"wires":[["d759b7e1377cf73d"]]},{"id":"db2739665070cbfd","type":"ui_chart","z":"829af3ee.a57c1","name":"","group":"aed2c45b95c6f503","order":4,"width":"6","height":"4","label":"角速度","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":850,"y":1660,"wires":[[]]},{"id":"59d48e3029faf484","type":"ui_chart","z":"829af3ee.a57c1","name":"","group":"aed2c45b95c6f503","order":5,"width":"6","height":"4","label":"トルク","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":850,"y":1700,"wires":[[]]},{"id":"4497f110769c4ec0","type":"ui_chart","z":"829af3ee.a57c1","name":"","group":"aed2c45b95c6f503","order":6,"width":"6","height":"4","label":"温度","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":"5","removeOlderPoints":"","removeOlderUnit":"60","cutout":0,"useOneColor":false,"useUTC":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"outputs":1,"useDifferentColor":false,"className":"","x":850,"y":1740,"wires":[[]]},{"id":"cd11f08432d136ac","type":"function","z":"829af3ee.a57c1","name":"角速度変換","func":"// 入力値を取得\nvar inputValue = msg.payload; // 入力値として0から65535までの数値が入ってくるものと仮定します\n\n// 0から65535の範囲を-30から+30の範囲に線形に変換する処理\nvar convertedValue = ((inputValue / 65535) * 60) - 30;\n\n// 変換された値を出力\nmsg.payload = convertedValue;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":710,"y":1660,"wires":[["db2739665070cbfd"]]},{"id":"d759b7e1377cf73d","type":"function","z":"829af3ee.a57c1","name":"1/10処理","func":"// 入力値を取得\nvar inputValue = msg.payload; // 入力値として0から65535までの数値が入ってくるものと仮定します\n\n// 1/10に変換する処理\nvar convertedValue = inputValue * 0.1;\n\n// 変換された値を出力\nmsg.payload = convertedValue;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":700,"y":1740,"wires":[["4497f110769c4ec0"]]},{"id":"c36bff986c6b9ae3","type":"function","z":"829af3ee.a57c1","name":"トルク変換","func":"// 入力値を取得\nvar inputValue = msg.payload; // 入力値として0から65535までの数値が入ってくるものと仮定します\n\n// 0から65535の範囲を-12から+12の範囲に線形に変換する処理\nvar convertedValue = ((inputValue / 65535) * 24) - 12;\n\n// 変換された値を出力\nmsg.payload = convertedValue;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":710,"y":1700,"wires":[["59d48e3029faf484"]]},{"id":"5a61cf62a8b994c2","type":"debug","z":"829af3ee.a57c1","name":"受信データ確認","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":340,"y":1400,"wires":[]},{"id":"143eb716e4ca74ec","type":"function","z":"829af3ee.a57c1","name":"角度変換","func":"// 入力値を取得\nvar inputValue = msg.payload; // 入力値として0から65535までの数値が入ってくるものと仮定します\n\n// 0から65535の範囲を-720(-4π)から+720(+4π)の範囲に線形に変換する処理\nvar convertedValue = ((inputValue / 65535) * 1440) - 720;\n\n// 変換された値を出力\nmsg.payload = convertedValue;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":700,"y":1620,"wires":[["18f9a3a97272bb94"]]},{"id":"f45f79b28204da59","type":"socketcan-in","z":"829af3ee.a57c1","name":"socketcan-out","config":"c4ed8d183dc6b2b8","x":740,"y":1160,"wires":[]},{"id":"5fafa1e822937959","type":"comment","z":"829af3ee.a57c1","name":"Send CAN frames payloads on CAN0","info":"","x":190,"y":1100,"wires":[]},{"id":"02d9ce1d8563dbb1","type":"link in","z":"829af3ee.a57c1","name":"CANデータ送信へ","links":["358b375f8bcf147d","99c8f6706859148f","9f6e322718b1abe1","05eef2189d4ae35f","e11de85eb331562d","f468e65e12cb5bc8","bd35cf143ad598d9"],"x":165,"y":1160,"wires":[["f45f79b28204da59","a645bb769d9892e5"]]},{"id":"a645bb769d9892e5","type":"debug","z":"829af3ee.a57c1","name":"送信データ確認","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":400,"y":1200,"wires":[]},{"id":"60cd64df26bfe89e","type":"change","z":"829af3ee.a57c1","name":"速度設定","rules":[{"t":"set","p":"payload","pt":"msg","to":"1200007f#1770000000000040","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":360,"y":480,"wires":[["bd35cf143ad598d9","829c8225d633c644"]]},{"id":"829c8225d633c644","type":"delay","z":"829af3ee.a57c1","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":650,"y":540,"wires":[["5435ed4a4363e2f8"]]},{"id":"bd35cf143ad598d9","type":"link out","z":"829af3ee.a57c1","name":"CANデータ送信へ","mode":"link","links":["d37a21bf35e2c904","02d9ce1d8563dbb1"],"x":635,"y":480,"wires":[]},{"id":"55041e4dcb8338f1","type":"function","z":"829af3ee.a57c1","name":"スライダーから角度を計算","func":"// msg.payloadから値を取得(本来なら4バイトだがスライダーにそこそこ合う形で角度を数値化)\nvar decimalValue = msg.payload;\n\n// 10進数の値をInt16Arrayに変換\nvar int16Array = new Int16Array([decimalValue]);\n\n// Int16Arrayの値を16進数の2バイト文字列に変換\nvar hexString = int16Array[0].toString(16).toUpperCase();\n\n// 変換後の値をmsg.payloadに設定\nmsg.payload = hexString;\n\n// 変換後のメッセージを出力\nreturn msg;\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":580,"y":740,"wires":[["2c0c474c730cb367"]]},{"id":"2c0c474c730cb367","type":"function","z":"829af3ee.a57c1","name":"エンディアン変換","func":"// 入力値を取得\nvar inputValue = msg.payload; // 入力値として0から65535までの数値が入ってくるものと仮定します\n\n// 整数を16進数の2バイトのリトルエンディアン値に変換\nvar hexValue = inputValue.toString(16);\n\n// 2バイトになるように左側に0を追加(必要であれば)\nwhile (hexValue.length < 4) {\n hexValue = '0' + hexValue;\n}\n\n// リトルエンディアンの順序に変更\nvar littleEndianValue = hexValue.slice(2, 4) + hexValue.slice(0, 2);\n\n// 変換された16進数の2バイトのリトルエンディアン値を出力\nmsg.payload = littleEndianValue;\nreturn msg;\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":820,"wires":[["74646c8e365330b3"]]}]
コードの読み込みについては、以下のサイトがわかりやすいです。 https://confrage.jp/node-redでフローの書き出しと読み込み/#toc1
ugoでは一緒にロボットを社会実装していく仲間を絶賛募集中です。
詳しくはこちら👇まで。