モブ沢工房

プログラミングとかLinux関連(特にOSSのグラフィックツール関連)とかレトロゲームとか3Dプリンタやら日曜大工等、色々。

Ender3 Neoにラズパイを接続し、Mainsail OSを導入してみた(その3・ レベリング編)

さてレベリングです。 3Dプリンタ単体で使っていた手順と似ていますが、Klipperではスクリューでの手動調整が遥かに便利になっています。

  1. プローブのZオフセット*1を導出・保存 (マクロ:PROBE_CALIBRATE)
  2. スクリューを回して可能な限り平坦に調整(マクロ:SCREW_TILT_ADJUST)
  3. プローブでベッドの局所的な歪みを調べ、高低マップ*2を作成・保存

ここでの手順は全てwebサーバのインターフェースで行います。

プローブのZオフセットを導出する

まずは、ホームボタンでホーム位置に戻す必要があります。

そしてweb制御画面のコンソール*3からPROBE_CALIBRATEを実行すると、ホーム位置に戻ってbl touchセンサーが反応したあたりで止まります。そして、ダイアログが出て入力待ちとなります。

ここで何をするのかと言うと、コピー紙を差し込んでの初期ノズル高さの調整です。3Dプリンタの調整ではおなじみのやつです。 コピー紙がカスカスに触れるまでノズルを下げて「確認」ボタンで設定を保存です

スクリューを回して可能な限り平坦に調整

注: これとハイトマップ作成は、ベッドをプリント温度(60度程度?)に温めてから行ったほうが、実際の印刷状況に近づいて良いです。

次はスクリューを回して可能な限り平坦に調整します。これを、今までは目視のカンでやっていましたが、Mainsailではプローブの値から計算して、各スクリューをどれだけ回せば良いかを時計の分で教えてくれます。なお、左手前のスクリューは基準スクリューとなるため、このスクリューに対する調整指示は出ません。 ので、予め平均的な位置に調整しておくのが良いかと思います。

さてこれを行うにはコンソールで「SCREW_TILT_ADJUST」を実行します。するとダイアログが現れ、前述のprinter.cfgで設定されたネジ位置(もしくは、ネジに近い適切に設定された場所)における高さをそれぞれ測って、回転すべき方向と量を計算して表示してくれます。回転量は時計の分です。

ここでスクリューを回すコツとしては…例えば「右に6分」回すとすれば、スクリューに向かって正面を0分と考え、右に6分回転=「逆に、向かって左側に6分のあたりに指を付け、真正面の0分に来るまで回す」という方法が、自分的にはわかりやすいと感じました。

指示に合わせて大体回したら、再度計測してもらいます。というのも調整する事自体により、傾きに偏りが出来ている可能性があるからです。そして調整したら再度計測。これを繰り返します。目安としては、全てのネジの回転量が5分位内に収まっていれば良いらしいのですが、私は2分以下まで追い込んでいます…

だいたい良くなったらOKでダイアログを抜け、設定を保存してklipperを再起動します。

高低マップの作成

次はコンソールではなく左のタブで高低マップ画面に切り替えます。そして、「校正」を押して、高低マップの測定=作成を開始します。 なお、この画面状態は既に高低マップが作成され、そして読み込み済みのときの状態です。読み込まれていないときは、ポリゴンの高低マップは表示されていません。

これはおまかせでよいので、楽です。適切にスクリュー調整が成されていれば、異様に傾いていたりはしないはずですし、ベッドの歪みに対しては、なすすべがありません。

高低差のmin/maxが0.1に収まっていれば良好なベッドと言えると思います。名称「default」でセーブしましょう。(初期名が既にdefaultのはずですが)

しかしながら、この設定を印刷前に適用しなければなりません。毎回起動するたびに高低マップ画面を開いてdefault高低マップをロードする…というのも、面倒くさいです。自動化したい。

検索によって知られる情報では、 defaultという名前の高低マップは起動時に自動的に読み込まれる …というのですが、どうも怪しいのですねぇ。というのも、起動するとこのポリゴンが表示されていないのですよ。しかし、後述するマクロ&スライサーソフト設定での自動適用を使用した時には、上に掲載したスクショのようにポリゴンが表示されている、というですね…*4

ですから、次回説明するスライサーソフトとの協調で開始時マクロ・終了時マクロを実装するときに、ついでにこの高低マップを読み込む処理をつけることになります。

次回予告

以上でレベリングは完了し、ようやく印刷が可能な状況になりました。しかし、私の環境ではこれだけでは最低限印刷が出来るだけで、純正ファームウェアとは違う挙動が若干不満でした。

例えば、印刷開始時にノズルとベッドの加熱が別々であるために余計な時間がかかる…とか、印刷終了時にベッドを前に出してくれないので、自分で出す必要があるなどです。これらは純正ファームでは出来ていたことですので、なんとかしたい。

次回は基本的なマクロ設定による定番の使いこなしを紹介して、それらの不満を解決して完全に純正ファームの上位互換の環境を目指します。

ではでは!

*1:bl touchセンサーの先とノズル先の高低差

*2:高低マップというのはベッドの微妙な高低差をメッシュ状に記録したマップで、プリント品質を上げるためのものだそうです

*3:なお、コンソールではTABキーで補完が効きますので、便利です。

*4:まぁそれ以前に、今ひとつ高低マップの効果が薄いようにも思うのですが😅

Ender3 Neoにラズパイを接続し、Mainsail OSを導入してみた(その2・ 設定編)

今回は、設定編、つまるところklipperのprinter.cfgを書く編となります。

表示の日本語化

さて、本題の前に忘れていましたが、表示を日本語化することが出来るので、それを説明しておきます。web画面右上にギアのアイコンの「設定変更」ボタンがありますので、それをクリック。すると次のような画面が出て、Language部分で日本語化出来ます。

ただし、これは痛し痒しと申しますか…検索して出てくる情報は英語のほうが多いので、むしろ英語画面のままで使っていたほうが解決に近づける、ということがあったりします。

私の記事ではとりあえずわかりやすさを優先して日本語化した状況で書いておきます。

printer.cfgとは何か

このprinter.cfgは、Mainsail OS内で ~/printer_data/config/に置くもので、デフォルトでは存在しません。ユーザーが使用したい3Dプリンタに応じて、自分で用意する必要があります。メジャーなプリンターは、~/klipper/config/に出来合いのものがプリセットとして存在していますので、それを名前を変えてコピーすればいいだけです。

このファイルが無くてはファームウェアを更新してもklipper側が制御方法がわからず使えないようです。

しかし、ネットで転がっているprinter.cfgはメジャーな機種でも多種多様で混乱します。というのも「ぼくのかんがえたさいきょうのprinter.cfg」を皆様が掲載されているので、それらはいろいろなカスタムが施されていて特殊なソフトウェアへの対応が成されていたりするのです。

これは初心者としては混乱しかしません。ましてや、Ender3 Neoのようなマイナー機種では尚更です。

そんなわけで苦心の結果見つけたEnder3 Neo用のprinter.cfgファイルがこれです。これをベースとします。 そう、これのままでは動かないんですねぇ…😭 でも、基本的なところは編集せずに使え、初心者として本当にありがたかったです。

github.com

そんなわけで元ファイルを見るにはリンクから飛んでいただき、単刀直入に私が調整して動かすに至った最も初期のprinter.cfgを掲載しておきます。これが、基本中の基本です。 今はさらにいろいろな独自の改良を加えていますが、それはまた後の章で紹介します。

とりあえず動くようになった版

試行錯誤の結果、先程のgithub版を色々変えてようやく動くようになった版です。今後、さらに書き加えていきますが、まずはこれが基本となっています。

# This file contains pin mappings for the stock 2020 Creality Ender 3
# V2. REPROGRAMMED FOR ENDER 3 NEO To use this config, during "make menuconfig" select the
# STM32F103 with a "28KiB bootloader" and serial (on USART1 PA10/PA9)
# communication.

# If you prefer a direct serial connection, in "make menuconfig"
# select "Enable extra low-level configuration options" and select
# serial (on USART3 PB11/PB10), which is broken out on the 10 pin IDC
# cable used for the LCD module as follows:
# 3: Tx, 4: Rx, 9: GND, 10: VCC

# Flash this firmware by copying "out/klipper.bin" to a SD card and
# turning on the printer with the card inserted. The firmware
# filename must end in ".bin" and must not match the last filename
# that was flashed.

# See docs/Config_Reference.md for a description of parameters.

[include mainsail.cfg]   # I added to deal with warning. https://www.reddit.com/r/klippers/comments/1bgmxjr/noob_with_an_ender_3_pro_with_bl_touch_i_got/

[stepper_x]
step_pin: PC2
dir_pin: PB9
enable_pin: !PC3
microsteps: 16
rotation_distance: 40
endstop_pin: ^PA5
position_endstop: -28
position_min: -28
#position_max: 235
position_max: 230
#position_max: 285
homing_speed: 50

[stepper_y]
step_pin: PB8
dir_pin: PB7
enable_pin: !PC3
microsteps: 16
rotation_distance: 40
endstop_pin: ^PA6
position_endstop: 0
position_max: 225
#position_max: 285
homing_speed: 50

[stepper_z]
step_pin: PB6
dir_pin: !PB5
enable_pin: !PC3
microsteps: 16
rotation_distance: 8
endstop_pin: probe:z_virtual_endstop
position_max: 255
position_min: -10
homing_speed: 4
second_homing_speed: 1
homing_retract_dist: 2.0

[extruder]
max_extrude_only_distance: 1000.0
step_pin: PB4
dir_pin: PB3
enable_pin: !PC3
microsteps: 16
rotation_distance: 32.473 # e-steps was 34.406 but 32.473 in creality sonic pad config
nozzle_diameter: 0.400
filament_diameter: 1.750
heater_pin: PA1
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PC5
control: pid
#pressure_advance = 1.0
# tuned @ 200C 12.02.2023
pid_kp = 24.974
pid_ki = 1.435
pid_kd = 108.636
min_temp: 0
max_temp: 265

[heater_bed]
heater_pin: PA2
sensor_type: EPCOS 100K B57560G104F
sensor_pin: PC4
control: pid
# tuned for stock hardware with 50 degree Celsius target
pid_Kp: 54.027
pid_Ki: 0.770
pid_Kd: 948.182
min_temp: 0
max_temp: 130

[fan]
pin: PA0

[mcu]
serial: /dev/serial/by-id/usb-1a86_USB_Serial-if00-port0
restart_method: command

[printer]
kinematics: cartesian
max_velocity: 300
max_accel: 3000
max_z_velocity: 5
max_z_accel: 100

[virtual_sdcard]
path: ~/printer_data/gcodes

[pause_resume]

[bltouch]
sensor_pin: ^PB1
control_pin: PB0 
#x_offset: -42
#y_offset: -10
x_offset: -39
y_offset: -12
#z_offset: 3

[safe_z_home]
home_xy_position: 117.5, 117.5 # Change coordinates to the center of your print bed
speed: 240
z_hop: 10                 # Move up 10mm
z_hop_speed: 5

[display]
lcd_type: st7920
cs_pin: PB12
sclk_pin: PB13
sid_pin: PB15
encoder_pins: ^PB14, ^PB10
click_pin: ^!PB2

[bed_mesh]
speed: 240
horizontal_move_z: 5
mesh_min: 15, 15
mesh_max: 188, 191
probe_count: 6,6
algorithm: bicubic
fade_start: 1
fade_end: 10
fade_target: 0

[screws_tilt_adjust]
#screw1: 70.5,37.5
screw1: 67.5,52.5
screw1_name: front left screw
#screw2: 240,37.5
screw2: 230,52.5
screw2_name: front right screw
#screw3: 240,207.5
screw3: 230,215.5
screw3_name: rear right screw
screw4: 67.5,215.5
screw4_name: rear left screw
horizontal_move_z: 10
speed: 50
screw_thread: CW-M4

[respond]
default_type: command
[pause_resume] recover_velocity: 50

#*# <---------------------- SAVE_CONFIG ---------------------->
#*# DO NOT EDIT THIS BLOCK OR BELOW. The contents are auto-generated.
#*#
#*# [bltouch]
#*# z_offset = 2.470
#*#
#*# [bed_mesh default]
#*# version = 1
#*# points =
#*#     0.217500, 0.255000, 0.237500, 0.167500, 0.170000, 0.247500
#*#     0.177500, 0.212500, 0.210000, 0.177500, 0.207500, 0.210000
#*#     0.162500, 0.202500, 0.185000, 0.175000, 0.162500, 0.205000
#*#     0.137500, 0.175000, 0.187500, 0.187500, 0.207500, 0.185000
#*#     0.147500, 0.200000, 0.205000, 0.190000, 0.180000, 0.195000
#*#     0.165000, 0.195000, 0.210000, 0.190000, 0.240000, 0.192500
#*# tension = 0.2
#*# min_x = 15.0
#*# algo = bicubic
#*# y_count = 6
#*# mesh_y_pps = 2
#*# min_y = 15.0
#*# x_count = 6
#*# max_y = 191.0
#*# mesh_x_pps = 2
#*# max_x = 188.0
#*#
#*# [bed_mesh <Test>]
#*# version = 1
#*# points =
#*#   0.337500, 0.345000, 0.260000, 0.285000, 0.265000, 0.425000
#*#   0.232500, 0.247500, 0.202500, 0.195000, 0.127500, 0.212500
#*#   0.085000, 0.112500, 0.107500, 0.342500, 0.007500, 0.082500
#*#   -0.055000, -0.010000, -0.072500, -0.002500, -0.057500, -0.027500
#*#   -0.167500, -0.155000, -0.217500, -0.210000, -0.217500, -0.157500
#*#   -0.245000, -0.250000, -0.335000, -0.327500, -0.385000, -0.312500
#*# tension = 0.2
#*# min_x = 15.0
#*# algo = bicubic
#*# y_count = 6
#*# mesh_y_pps = 2
#*# min_y = 15.0
#*# x_count = 6
#*# max_y = 191.0
#*# mesh_x_pps = 2
#*# max_x = 188.0

動くようにするために何を変えたのか?

先頭に [include mainsail.cfg] を追加した

これがないと、マクロが見つからない系のエラーが出る為。

screws_tilt_adjustセクションの内容を改変

この値がエクストルーダーの動作範囲外であったため、拾ってきたままのファイルでは動作しなかったです。なお、この項目は本来は調整ネジの直上にセンサープローブが来るように設定するべきなのだけど、そもそも私のEnder3 Neoでは物理的に、X軸方向に最大に動かしても、向かって右側のネジの上にはプローブが来ないのです。

そのため、ネジより中心方向に向かって2cmほど内側にオフセットして値を設定しておきました。

bl_touchセクションの内容も改変

また、前述のスクリュー周りのエラーを見るに、作者の方が間違えたというのは考えづらく…

中華メーカーあるあるで「何かエクストルーダー周りの仕様が、同じ型番にも関わらず何の広報もなくちょくちょく変化している場合」も考え、全ての値が個体差がある可能性も踏まえ、ノズルオフセット(x_offset / y_offset)も自分の個体に併せて導出し直しました。

stepper_x / stepper_yの内容を改変

同時に、個体差(リビジョン差?)も考え、動作範囲となるstepper_x・stepper_yのstepper_maxも実際の私の所有しているものに合わせて調整しておきました。

次回予告

これでとりあえずエラーがでなくなり、エクストルーダーをホームに動かしたりすることが出来るようになりました。しかし、印刷にはまだもう1手間「レベリング」が必要です。しかし、Klipperの便利機能によりレベリング調整は劇的に楽になっています。

次回は「その3・レベリング&初めての印刷編」となります。

Ender3 Neoにラズパイを接続し、Mainsail OSを導入してみた(その1・導入編)

今更ながらEnder3 Neoにラズパイ3を接続し、それにMainsail OSを載せて制御してみました。 結果的には極めて満足行くものになりましたが、この取り合わせ、今ひとつメジャーではない?ためにかとても資料が少なく、色々と大変でした。

それで数回にわけて備忘録としてこのブログに書いておこうと思います。

前置き・Ender3 Neoについて

Crealityの大人気廉価FDM方式3Dプリンタ・Ender3の後継…のようですが、後継の中でも廉価版で、かつユーザーが少ない感じがしています。 なお、とても重要なことですが、Ender3 V2 Neoとは違うものです。Ender3 V2 Neoは結構メジャーですが、Ender3 NeoはEnder3シリーズであるにも関わらず、マイナーです…😭

このため、「その2」で後述するprinter.cfgの中で適合するものを探すのに、結構手間取りました。

前置き2・接続方式

Ender3 Neoの正面にはUSB microB端子が存在しており、そこからラズパイにUSB A端子で接続します。つまりは(当たり前ですが)3Dプリンタはデバイスで、ホストがラズパイです。

ただしかし、ネットを見るとこの方式でEnder3をラズパイに接続してしばらく使っているうちにラズパイが壊れたという話を散見します。 どうも、USBの電源ラインが悪さをするらしいのです。不確かですが、たしかにあり得る話です…そこで念の為、わざわざジャンクのUSB端子を用いて保護用の「USBコンドーム」を自作しました☺️

usb近藤さん

ジャンパで実装したのは将来、不要になっても何か別の用途に使えないかな〜という理由です。

赤のVCCラインだけ切り離しています。これでOK!(のはず) 今の所、壊れていません。って日が浅いですけど😅

Mainsail OSのインストール

Mainsail OSはRaspiイメージャーの他のOSから選択することが出来、すごく簡単に入ります。ここは、何も説明する必要がありませんが、例によってイメージ書き込み前に

  • SSHをONにする (重要)
  • ユーザー名・パスワードをデフォルトのままにせず、自分で設定し直す
  • 無線LAN設定もしておくと便利

です。特にSSHカスタムファームウェアのビルドで結局必要になると思います。まぁ、キーボードとモニタを接続してもいいですけどね…😅

このMainsail OSとはLinuxディストーションの一つでもあり、

  • 制御システムの「Klipper」
  • webインターフェースの「Moonraker」

その他諸々を最初からプリインストールしてくれて、素人でも起動するだけで全ていい感じに設定されていて、いきなりサーバも起動しているという、とても便利な代物なのです。

さらには制御に必須のカスタムファームウェアを生成するためのコンパイラやmakeシステムなども搭載してくれているという、初心者でも楽しめるとてもありがたいOSなのです!

これさえあれば、.gcodeファイルの入ったsdカードを持ってうろつく必要なく、webブラウザから.gcodeファイルを送り込んで印刷出来ます。また、ラズパイに接続したカメラを用いてブラウザからプリントの進行を監視することも、これまた簡単に出来ます。非常に捗ります。

その他、必要なもの・設定としては

  • webブラウザの走る、設定・制御用のPC。言うまでもなくデスクトップLinuxでOK。
  • SSH接続のためのターミナルソフト - Teraterm? Linuxなら普通のターミナルでOK
  • ラズパイの固定IP設定 - ルータからMACを見ての固定DHCP割当が便利

これらを準備しておくとよいでしょう。

Ender3 Neo用のファームウェアの作成

さて、実際、つなげるだけでは制御できません。Ender3 NeoのファームウェアをMainsail(Klipper)カスタムのklipper.binに焼き直すことで、ラズパイから制御出来るようになるのです。

専用ファームウェアは、mainsail osの ~/klipperディレクトリにおいて、klipper.binを自分でmakeする必要があります。このとき、全てCUIで行うため、SSHでやると楽です。

makeの手順は、make menuconfig -> curses的UIで色々設定 → makeでklipper.binを生成するといったものです。

make menuconfigでは、curses的なキャラクタの組み合わせの擬似GUIがコンソールに出現します。それに対してカーソルキーでカーソル移動、スペースキーでチェックをOn/Off(クリア)します。

設定内容は、次回説明する、githubで拾ってきたEnder3 Neo 用のprinter.cfgのコメントにあった指示に従って…

  • Enable extra low-level configuration optionsをオフ(クリア)する。
  • Processor modelが STM32F103であること。
  • Bootloader offsetが28KiBであること。デフォは8KiBだったりする。
  • SerialはUSART1 PA10/PA9であること (デフォはUSB (on PA11/PA12) であったりする)

以上を設定・確認したら、Qキー -> Save configuration でYでコマンドラインに戻る -> make実行 -> これで~/klipperoutディレクトリにklipper.binが出来て、終了です。

このklipper.binをPC側に取り出すのが、Linuxは簡単ですが、windowsではwinscpを入れるとか、ラズパイ側にUSBメモリを入れてmountコマンドでマウントして書き込み…等、慣れていないと結構面倒かもしれません。

私のオススメは、SSHによってMainsailの~/printer_data/config/ディレクト*1に、klipper.binをコピーしてしまうことです。そして、PC側のwebブラウザからMainsailを開き、MACHINE(機器)タブの設定ファイル一覧にklipper.binが出現しているため、これを選択だけ*2すると上段にダウンロードアイコンが出現しますので、ダウンロードします。

PCにはzipファイルとして送られてくるので、その中から取り出せばOKです。

ファームウェアの書き込み

klipper.binが出来たら、Ender3 Neoに付属していた8GBのmicroSDFAT32でフォーマットし、klipper.binだけを書き込んでプリンタをブートします。しばらくすると、ファームウェアがKlipper用に変更されます。

この8GBというところが重要で、MacOS等の環境によってはこれより大きな容量だと上手く行かないようです。というのも、Ender3側では、クラスタサイズが4KBのFAT32しかファームウェア書き換えに対応していないらしいのです。Linuxなどではクラスタサイズを自在に変えられる(ハズ)ですが、MacOSでのFAT32フォーマットはMS推奨のデフォルトのクラスタサイズしか使えず、その時、容量8GBがクラスタサイズ4KBになる最大の容量…らしいのですね。

らしいというのは実際はよくわかりません😅 私はLinuxでしかも8GBの添付のものを使ったので、無事成功しました。

単体のプリンタに戻したくなったら、公式サイトのROMファイルで同じようにするだけなので、大丈夫です(多分

書き込むのは最初だけなので、ファームウェアが更新されたらsdカードを抜いてください。

さて、ここで一区切りとしますが、これだけではプリンタを認識してくれません。klipperシステムの中核となる「printer.cfg」ファイルを適切に設定する必要があります。 これはまた次回。

ではでは!

*1:基本的にMainsail OSでは、printer.cfgなどの主要設定ファイルは~/printer_data/configディレクトリに置くことになります。そのため、とても重要なディレクトリです。

*2:開いてしまうとバイナリなのにテキストとして処理され異常な文字列のファイルとして画面に出てしまい、意味がないです。そして、間違えて保存ボタンを押すとヤバイ気が…

xubuntu 22.04にgo言語の為にliteIDEを入れてみた話

xubuntu 22.04のためにrust用の環境を整えていて、ふとgoも、以前ほんのチョット試食したきり最近やってないけどどうなんだろうと思い始めました。 snapでIDEのgolandがあるのを知って入れてみたんですが無料体験版で年10000円…まぁ出来がよいので払っても良いかなとも思いましたが、しかし使うとも限らないものに10000円はな〜と思ってliteIDEをappImageで入れてみました。

なお、snapにもliteIDEはありますが日本語ではフォントが豆腐になる上にコードエディタでIMが起動しません。イラネ

しかし、そこからが厳しい罠だった!

appImage版liteIDEは快調に起動してGUIも美しく日本語も入力できご満悦…だったのですが、delveデバッガがシンボルが読めない的なエラーが出てデバッグが出来ないのですな。

そこで色々調べてやっているうちにふと思いました。もしかしてこれはgoのバージョンが悪いのでは…と。

22.04のapt標準で入るのはgolangとdelve共に1.18です。そこでgoを1.23に上げてみました。すると、goのsymlinkがPATHに作られない…どうも手動でやらねばならぬ模様。

まぁいいや。

そして、liteIDEでビルドしてデバッガを動かすとdelve1.18で1.23のgoには古すぎる的なメッセージが出て動きません。しかしながら、バージョンを無視するオプション"--check-go-version=false" を付加してみた所… デバッグできることはできる!

ブレークポイントで止まります。しかし、そこからステップ実行が出来ない。continueしか。 さらに、現在の実行中の行も判別出来ないらしい。

仕方ないのでdelveもアンインストールし、手動で最新のdelveをgo install github.com/go-delve/delve/cmd/dlv@latest でインストール。これで$GOPATH/binに出来たdlvをsymlinkしたら…

まともにデバッグができるじゃないですか😭感動の涙!

え、四の五の言わずにVSCodeを使えですと?それは宗教上の理由で無理なのですよ…まぁ、本当の本当に追い込まれたら使いますけどね😆

ではでは!

へんくまさんを表示するコード

電気ポポロン(@hkppamp)さんの「へんくまさん」を出来る限り小さいデータ量で表示すべく!

4ビットの空白長・4ビットのデータ長でエンコードし、改行はこの場合のデータカウントが最小2・最大5である事を利用して別で2ビットで表現という、無理矢理感があるコードですが…😅

PICのような演算能力に限界がある環境を主眼において書いたわけです。PC上で実行してテストするためのコードを張って置きますが簡単にPICに持っていけそうな予感…

そういう環境では演算ライブラリが大きくなってしまうために、こう言うのが結局最小になるのではないかーと思いまして、思いついてしまったのでとりあえずやってみた感☺️ もともとは128バイトだったのが合計69バイトに減りました

まぁ、さらに上部分は左右鏡像反転してデータを削減し、下のしっぽの部分だけ反転せずそのまま描画、という手もありそうですが、今回はここまでです。気が向いたらやる鴨?

#include<stdio.h>

void msg(char v) {
    putchar(v);
}

#define NEWLINE '\n'

// (4bit spc | 4bit data length)
static const unsigned char HenkumaData [] = {148, 196, 129, 65, 161, 65, 129, 65, 161, 65, 129, 31, 3, 17, 114, 240, 82, 97, 240, 161, 81, 70, 102, 81, 21, 240, 197, 81, 240, 193, 21, 65, 114, 129, 69, 81, 87, 40, 81, 98, 240, 130, 143, 8, 97, 33, 240, 17, 81, 209, 33, 241, 113, 106, 225, 145, 35, 161, 225, 23, 31, 1, 241, 113};  

// 2bit packed. data counts for each lines.
static const unsigned char NLData [] = {42, 89, 121, 182, 128}; 

int main(void) {
    unsigned char t;
    unsigned char lcnt = 0;
    char lmax_idx = 0;
    char lmax_cnt = 1;
    unsigned char lmax_src = NLData[lmax_idx];
    unsigned char lmax = (lmax_src >> 6) + 2; // Minimum 2 datas.
    lmax_src <<= 2;

    for( unsigned char i = 0; i < sizeof(HenkumaData); i++ ){
        unsigned char d = HenkumaData[i];
        if( lmax == lcnt ){
            msg(NEWLINE);
            lcnt = 0;
            lmax = (lmax_src >> 6) + 2;
            lmax_src <<= 2; 
            if (++lmax_cnt == 4) {
                lmax_src = NLData[++lmax_idx];
                lmax_cnt = 0;
            }
        }

        for(t=0; t < (d>>4); t++)
            msg(' ');
        for(t=0; t < (d & 0x0f); t++)
            msg('*');
        lcnt++;
    }

    msg(NEWLINE); // last newline

    return 0;
}

ではでは!

ubuntu 22.04でのmu-editorについて

python開発においてgvimvim-lspなど入れているのですが、vimにデバッガ拡張を入れるのはどうしたものか…と思っていてスタンドアロンで動くデバッガを探しています。 (なお、VSCodeは宗教上の理由でできるだけ使いたくない上に、vimspectorも結局vscode-pythonだったりするし…)

python2時代はここでwinpdbというものがあったのですが、これはディスコンされて今はwinpdb-rebornになっているのですが…これまたpipで入れてみた所、ubuntu 22.04ではrpdb2.pyが古いらしく?動きません。ちょっと強引に弄って見たら最初のエラーは消えたのですが、Xウィンドウが無いなどと言い出し、かなり手強い感じがあるため…放置です。

そこで別のエディタをデバッガとして使う作戦!

便利なthonnyですが、これRaspberry pi picoで使いたいのでいちいちホスト環境を切り替えたくはないのでさらに調べた結果、mu-editorというものがあることを知りました。

しかしこれまた動かない😭 …ですが、とても簡単な修正で動いたのでメモしておきます。

修正箇所

/usr/share/mu-editor/mu/interface/main.py のself.moveの引数が整数でないためにエラーで動作しないのです。故に、演算子を整数演算の「//」に変更します。

    def autosize_window(self):
        """
        Makes the editor 80% of the width*height of the screen and centres it.
        """
        screen = QDesktopWidget().screenGeometry()
        w = int(screen.width() * 0.8)
        h = int(screen.height() * 0.8)
        self.resize(w, h)
        size = self.geometry()
        self.move((screen.width() - size.width()) // 2,
                  (screen.height() - size.height()) // 2)

こんだけ〜

その他のGUI版デバッガになりうるものについて

python標準とも言える簡易IDEのidleでも行けますが、これはさらにデバッガのUIが特殊なので、アレです(しかし、何もせずに正常に動くのは好印象)

pythonIDEのeric6というのもありますが、実はこれも動きません。何なんだよ22.04…なお、

askubuntu.com

を適用すれば一応動くようにはなりますが、それでもまだ何か例外を出しているしそもそも動作が怪しいです。

…まぁ正直ね、もうvscodeで良いかもしれない(弱気)

snapでPyCharmという手もあるか…

単体のソースレベルデバッガとしてはpudbというものもありますね。これなかなか良いのです。良いのですが… これはターミナルにもよるのかもしれませんが、日本語コメントや文字列に遭遇すると高確率で画面が破損します。オマケに画面が色々枠になってて見づらい。

そこで単体デバッガとして久々にipdbを入れてみましたが…こんなに良かったっけ?😳 というほど好感触です。

もう全部ipdbで良いんじゃないかな?!

ではでは!

PILのImageのresizeでIOErrorが出た件

前置き

先に描いておきますがプリンタにもPILにも問題は無く、自分のポカミスですが備忘録と戒めとして書いておきます。

私はブラザーのインクジェット複合機DCP-J940Nを使用していて、これはftp機能もあるスグレモノですごく安く買った気がするのですが、10年近く愛用しています。もっぱらスキャナーとして…というか、プリンタ部分はもはや使用しておらず、インク検出部分に銅箔テープを張って常に満タンに見せかけていますw

使い方としては、まず画像をスキャンし… pythonの自作スクリプト「picture_loader.py」で、ftpで自動接続して「今まで読んでいない画像」があればカレントディレクトリにコピーし、読んだ画像のファイル名を~/local/share/picture_loader.jsonに書き出すという超カンタンな仕組みで使っています。

歪み問題とIOError問題

さて最近になってこのスキャナが実は縦(長辺方向)に少し長く歪んでいる事に気づきました。 何かスキャンした絵の印象が違うとは思っていたのですが、カッティングシートのグリッドをスキャンして分析した所、本当にわずかに長辺方向が長いのです。およそ2%弱程。 (なお、カッティングシートを90度回転させて検証しても同じ結果であったため、カッティングシートの方が歪んでいるということは無いです)

そこで前述のスクリプトpicture_loader.pyに自動で縮小する仕組みをつけようと思い至りました。

仕組みとしてはこれまた超シンプルで、テンポラリにダウンロードしたjpgをPIL.Imageオブジェクトにしてresizeで縦を2%縮小してカレントディレクトリにsaveするというだけのものです。

ところがここで問題発生です。resizeメソッドを実行すると

IOError: image file is truncated

というエラーが出るようになってしまいました。

ググった所、結構よくある症状らしくあちこちで解決策が取られていました。最も一般的で私の所でも一応機能したのが

from PIL import ImageFile
ImageFile.LOAD_TRUNCATED_IMAGES = True

とする解決策。これで一応例外は発しませんが、画像下部は16ドットほどゴミデータになっている。

一説ではこの現象、巨大すぎる画像で起きがちで、PILの遅延読み込みが原因ではないか説があります。でも私のはA4を300dpiスキャン程度なんですよねぇ…

試した解決策と、本当の原因

以下、色々試した結果です。

  • 一旦メモリに読み込み、ByteIOにしてからPIL.Image作成 → resizeでIOError発生
  • OpenCV2経由でPIL.Imageに変換 → 走るが、画像下部にゴミデータ
  • Numpy経由でPIL.Imageに変換 → resizeでIOError発生
  • SubprocessでImageMagickを起動、一時ファイルをconvertコマンドで縮小 → 走るが、画像下部にゴミデータ
  • Image.new("RGB", (w, h)) で同寸法の画像を生成して、そこに元画像をpasteして新画像をresize → pasteでIOError発生

これはもう画像が元々こわれてんじゃねーの…?🤔 とプリンタを疑ってしまいましたが、

さにあらず

実にくだらないポカミスでした。該当箇所は次のようなコードになってましたが

                with open(cpath, 'wb') as ofp:
                    print("TRANSFER : %s" % cf)
                    ftp.retrbinary("RETR %s" % cf, ofp.write)

                    if scaling_factor is not None:
                        scaled_name = get_scaled_name(cpath, destdir)
                        scale_picture(cpath, scaled_name, scaling_factor)

そう、スケール処理の部分のインデントがwith内側と同じになってしまっている。つまり、ファイルがclose()される前に読み込んでいるわけです。 だから、ImageMagickですら同じ結果になっていたわけです。

つまるところ

  • データが実際に不完全
  • PILの遅延読み込み処理が要因の一つ

…というのは例外の直接の発生要因としては実際正しいのですが、今回の場合その本当の原因はボケーッと書いたコードにある。

という、私の場合はしょーもない原因の判明に至ってしまいましたが…😅

このエラーが出た場合は本当にファイルが完成しているのか、まずは調べたほうがいい場合がありそうです。

ではでは!