生成AIを御する | virt_flyのブログ

virt_flyのブログ

フライトシミュレーターソフトのFlightGearで仮想飛行を楽しむブログです。

Raspberry Pi Picoで生成AIにドライバを書かせる

 

Pico用ST7789ドライバをつくる

 

■Pico用のST7789ドライバがない?

 

Raspberry Pi Pico Wに2.0インチのTFTディスプレイをつなごうとしたら、ST7789のドライバがないんですね。これまでも、そんなことがあったし、以前はあったのにドライバがThonnyのパッケージ管理に出てこなくなったということも。

 

Pico 2が販売されるようになったので、すっかり過去の物扱いだとかマイナーなんてことでもなさそうだし、なにかよくわかりません。

 

最初は、パッケージ管理にそれらしいものがでてきたのでインストールしたものの動かず、生成AIに尋ねたらCircuitpython用のドライバだと言われてしまい、次に教えられたレポジトリに行ったらビルドが必要ものしかみつからない。結局、ドライバの全文を示せと言ってやってようやく手に入れたかと思えば動かない。驚いたのは、動かないと言ったら、生成AIの奴が生意気にも環境にあったドライバを書いてやると言うんです。

 

■生成AIの迷走

 

まあ、それで書かせてみたのですが、たった一言”Hello”と表示させるのにすったもんだ。複数の生成AIを使ったもののさっぱりだめで、「ST7789じゃない他のドライバだ」とあちこち引っ張り回され堂々めぐりを繰り返されたり、気が付けば途中で勝手にピン配置を変えられたり、あげくRaspberry Pi Zeroで表示できたと伝えているのに、ハードが壊れているとのたまう。最後には7ピンのこのタイプはZero用で8ピンのArduino用を購入せよとまで言われました。

 

ちなみに、これはと思う製品が見つかればチェックしてやるので販売サイトのURLを知らせろというので教えたら、これはダメだというのです。実はPicoで動かせた実例の中で示された購入先の物だったので、そのことを伝えたら態度がコロッと変わり、動くと言い出す始末。

 

Pico ST7789ドライバで表示できた画面

↑7ピンの2.0インチTFT液晶に図や文字が表示できた

 

ほぼ諦めかけていましたが、試行錯誤の中で一瞬色塗りができたり、文字が表示されたこともあったので、全くダメというのも一概に言えない気がしてしつこく試した結果、4日目にして描画、文字表示に成功しました。諦めておれば、それまでの3日間を無駄にするところでした。やればできる子じゃないの。

 

■ドライバが書けたのは

 

成功したのは、まず第1に目的と使用するものの特徴、他で成功した例などを的確に示すことを心掛けたとき、色塗りに成功したのです。その後、過大な要求(航空機のPFDを一気に作ること)をしたことで、またまた迷走することになりましたが。

 

第2は、迷走に入る前の時点に立ち戻って、そこから再挑戦したこと。少しつまづきはあったもののあっさりと目的の描画と文字表示ができるようになったのです。

 

細かな教訓も多々ありますが、なによりも大事なのは、使い手の的確な指示、迷走したらチャラにして迷走前に戻って再チャレンジ、また無駄に時間を費やさないためには、迷走しそうな気配があれば早めに指摘し、発想を変えることを促すことであると感じます。

 

■使い手がアホなら生成AIもアホになる

 

生成AIの迷走を悪し様にあげつらいましたが、実のところはどんなに優れた道具も、それを生かすも殺すも使い手次第、ということの典型です。使い手がアホなら生成AIもアホになるわけで、責任は使い手にあることを、今回よく思い知らされました。

 

・的確な指示

プログラミングに生成AIは使えるなと思う反面、気を付けたいこともあります。いつまでも文句を言うこともなく、結論まで付き合ってくれることはありがたい生成AIなのですが、一歩間違うと延々とおかしな方向に突き進むことがあります。都度都度、的確な指示を出してやることが大事であり、最初から的確、明瞭な指示を出しておくことです。。

 

・早目の指示

タイパのご時勢です。無駄に時間を浪費しないためにも、明らかに間違っていることはもちろんですが、勘であっても何か違うような気がするときは早目に指摘してやった方がよいでしょう。一気に解決に向かうことが、度々ありました。

 

・生成AIの誤りを正す

生成AIも勘違いします。ピン配置を間違えていたり、描画の際の左右を間違えるということもあります。こちらの要望すらたまに忘れたりということもありましたから、遠慮せずに指摘してやらないといつまでもなおらないことになります。

 

・時々プログラム全文を書かせる

生成AIは、プログラムの修正点だけを示してくることがありますが、挿入個所の誤りやインデントの乱れを起こす原因になります。プログラムの全文を書かせることも大事です。

 

・異なる生成AIを使うのも手

使い手の指示の出し方に負うところ大というのが持論ですが、上手くいかないときは別な生成AIに質問するのも手だと思います。得手不得手もあるのか、一気に解決に至ることも少なくありません。

 

・あきらめないこと、疑問に感じること

生成AIを使って成功するには、諦めないことも重要です。時間制限などの制約がないわけではありませんが、文句も言わず(高ビーなところはある)、解決、あるいは結論の出るまでつきあってくれるのですから。そのためにも、疑問を大事にすべきと思います。生成AIの性急な結論に惑わされず、本当に壊れているのだろうかとか、ある程度成功しているのだからできないのは発想の転換が必要ということではないかとか、実際に成功しているといいながら片方でこれにはできないと矛盾したことを言っていないか、など。素人なりに頑張ってみたいと思います。

 

生成AIの進歩は急速で、ブログに書くのが追い付かないくらいのスピードです。描画機能が日々充実していくのに驚いた半面、的確なプロンプトを書くのに難儀したこともありましたが、逆に余計なお節介も困りもの。最近はそれも減り、結構アドバイスが的を射たものになってきたように思います。生成AIの進歩が楽しみですね。

 

PicoとTFTディスプレイを接続した実験

↑ようやくPico用に最低限の機能を備えたst7789ドライバができた

 

 

【作成したドライバst7789.pyの全文】

 

from machine import Pin import time

BLACK=0x0000
WHITE=0xFFFF
RED=0xF800
GREEN=0x07E0
BLUE=0x001F
YELLOW=0xFFE0
CYAN=0x07FF
MAGENTA=0xF81F


class ST7789:

  def __init__(self,spi,width,height,reset,dc,cs):

    self.spi=spi
    self.width=width
    self.height=height
    self.reset=reset
    self.dc=dc
    self.cs=cs

    self.cs.init(Pin.OUT,value=1)
    self.dc.init(Pin.OUT,value=0)
    self.reset.init(Pin.OUT,value=1)

  def cmd(self,c):

    self.cs(0)
    self.dc(0)
    self.spi.write(bytearray([c]))
    self.cs(1)

  def data(self,d):

    self.cs(0)
    self.dc(1)
    self.spi.write(bytearray([d]))
    self.cs(1)

  def reset_display(self):

    self.reset(0)
    time.sleep_ms(50)
    self.reset(1)
    time.sleep_ms(50)

  def init(self):

    self.reset_display()

    self.cmd(0x11)
    time.sleep_ms(120)

    self.cmd(0x36)
    self.data(0x00)

    self.cmd(0x3A)
    self.data(0x55)

    self.cmd(0x21)

    self.cmd(0x29)
    time.sleep_ms(20)

  def window(self,x0,y0,x1,y1):

    self.cmd(0x2A)
    self.data(x0>>8)
    self.data(x0&255)
    self.data(x1>>8)
    self.data(x1&255)

    self.cmd(0x2B)
    self.data(y0>>8)
    self.data(y0&255)
    self.data(y1>>8)
    self.data(y1&255)

    self.cmd(0x2C)

  def pixel(self,x,y,color):

    self.window(x,y,x,y)

    hi=color>>8
    lo=color&255

    self.cs(0)
    self.dc(1)
    self.spi.write(bytearray([hi,lo]))
    self.cs(1)

  def fill(self,color):

    self.window(0,0,self.width-1,self.height-1)

    hi=color>>8
    lo=color&255

    buf = bytearray(512)

    for i in range(0,512,2):
      buf[i]=hi
      buf[i+1]=lo

    self.cs(0)
    self.dc(1)

    pixels=self.width*self.height

    for _ in range(pixels//256):
      self.spi.write(buf)

    self.cs(1)

  def fill_rect(self,x,y,w,h,color):

    self.window(x,y,x+w-1,y+h-1)

    hi=color>>8
    lo=color&255

    buf=bytearray(w*2)

    for i in range(0,w*2,2):
      buf[i]=hi
      buf[i+1]=lo

    self.cs(0)
    self.dc(1)

    for _ in range(h):
      self.spi.write(buf)

    self.cs(1)

  def hline(self,x,y,w,color):
    self.fill_rect(x,y,w,1,color)

  def vline(self,x,y,h,color):
    self.fill_rect(x,y,1,h,color)

  def rect(self,x,y,w,h,color):

    self.hline(x,y,w,color)
    self.hline(x,y+h-1,w,color)
    self.vline(x,y,h,color)
    self.vline(x+w-1,y,h,color)

  def line(self,x0,y0,x1,y1,color):

    dx=abs(x1-x0)
    dy=abs(y1-y0)

    sx=1 if x0 <x1 else -1
    sy=1 if y0 <y1 else -1

    err=dx-dy

    while True:

      self.pixel(x0,y0,color)

      if x0==x1 and y0==y1:
        reak

      e2=2*err

      if e2>-dy:
        err-=dy
        x0+=sx

      if e2<dx:
         err+=dx
        y0+=sy


# ---------- フォント ----------

  font = {

"A":[0x18,0x24,0x42,0x7E,0x42,0x42,0x42,0],
"B":[0x7C,0x42,0x42,0x7C,0x42,0x42,0x7C,0],
"C":[0x3C,0x42,0x40,0x40,0x40,0x42,0x3C,0],
"D":[0x78,0x44,0x42,0x42,0x42,0x44,0x78,0],
"E":[0x7E,0x40,0x40,0x7C,0x40,0x40,0x7E,0],
"F":[0x7E,0x40,0x40,0x7C,0x40,0x40,0x40,0],
"G":[0x3C,0x42,0x40,0x4E,0x42,0x42,0x3C,0],
"H":[0x42,0x42,0x42,0x7E,0x42,0x42,0x42,0],
"I":[0x3C,0x08,0x08,0x08,0x08,0x08,0x3C,0],
"J":[0x1E,0x04,0x04,0x04,0x44,0x44,0x38,0],
"K":[0x42,0x44,0x48,0x70,0x48,0x44,0x42,0],
"L":[0x40,0x40,0x40,0x40,0x40,0x40,0x7E,0],
"M":[0x42,0x66,0x5A,0x5A,0x42,0x42,0x42,0],
"N":[0x42,0x62,0x52,0x4A,0x46,0x42,0x42,0],
"O":[0x3C,0x42,0x42,0x42,0x42,0x42,0x3C,0],
"P":[0x7C,0x42,0x42,0x7C,0x40,0x40,0x40,0],
"Q":[0x3C,0x42,0x42,0x42,0x4A,0x44,0x3A,0],
"R":[0x7C,0x42,0x42,0x7C,0x48,0x44,0x42,0],
"S":[0x3C,0x42,0x40,0x3C,0x02,0x42,0x3C,0],
"T":[0x7F,0x08,0x08,0x08,0x08,0x08,0x08,0],
"U":[0x42,0x42,0x42,0x42,0x42,0x42,0x3C,0],
"V":[0x42,0x42,0x42,0x42,0x42,0x24,0x18,0],
"W":[0x42,0x42,0x42,0x5A,0x5A,0x66,0x42,0],
"X":[0x42,0x42,0x24,0x18,0x24,0x42,0x42,0],
"Y":[0x42,0x42,0x24,0x18,0x08,0x08,0x08,0],
"Z":[0x7E,0x02,0x04,0x18,0x20,0x40,0x7E,0],

"0":[0x3C,0x46,0x4A,0x52,0x62,0x46,0x3C,0],
"1":[0x08,0x18,0x28,0x08,0x08,0x08,0x3E,0],
"2":[0x3C,0x42,0x02,0x0C,0x30,0x40,0x7E,0],
"3":[0x3C,0x42,0x02,0x1C,0x02,0x42,0x3C,0],
"4":[0x0C,0x14,0x24,0x44,0x7E,0x04,0x04,0],
"5":[0x7E,0x40,0x7C,0x02,0x02,0x42,0x3C,0],
"6":[0x1C,0x20,0x40,0x7C,0x42,0x42,0x3C,0],
"7":[0x7E,0x02,0x04,0x08,0x10,0x20,0x20,0],
"8":[0x3C,0x42,0x42,0x3C,0x42,0x42,0x3C,0],
"9":[0x3C,0x42,0x42,0x3E,0x02,0x04,0x38,0]

  }

  def char(self,x,y,ch,color):

    if ch not in self.font:
      return

    data=self.font[ch]

    for row in range(8):

      line=data[row]

      for col in range(8):

        if line&(1<<(7-col)):
          self.pixel(x+col,y+row,color)

  def text(self,x,y,string,color):

    for i,ch in enumerate(string):
      self.char(x+i*8,y,ch,color)

 

ST7789 driver for Raspberry Pi Pico
Written with assistance of ChatGPT
Public Domain / MIT License