また長い期間開いてしまいましたが、色々と勉強した結果、以前作成したこれが
こうなりました^^
前回ESP8266で画像表示まで到達できましたが、あれでは全然やりたいことが表現できませんw
これが実現できたのはLovyaGFXというライブラリーに出会ったからです^^。
まずは前回画像表示が出来て大喜びだったのですが、画像表示が当たり前に出来るようになるとその表示速度が気になり始めます^^;
やはり、これでは遅すぎます^^;
で、今まではグーグル等で普通に検索していたのですが、なんの拍子にそう思ったのか忘れましたがツイッターで検索してみました。すると超高速で描画されている動画発見w
で、ツイートを見てみるとLovyanGFXというライブラリーを使用されているとのこと。
早速そのライブラリーを使ってみることに!
とりあえず分からないなりにこんな感じなのかな?とスケッチを修正して動かしてみたものがこちら
ちょっww色などはともかく超高速ww複数枚の画像を切り替えて表示されているのですがなんちゅう早さww
で、これをたまたまツイッターでこんなに高速に表示されます。色がおかしいのでまだまだ勉強必要です。とツイートしたのですが、するとなんとこのLovyanGFXのライブラリーを作られたご本人からリプライを頂き、色の調整の仕方を教えて頂きました!
その際、SPIFFSからの画像表示についても質問してみたのですが、するとありがたい事にその方法についても教えて頂けました。
まず一番初めに教えて頂いたコマンドdrowJpegFileを試してみたのですが
こんな感じで以前の画像が残っているうえに表示したい画像が線状の表示でまともに表示されていませんでした。
するとらびあんさから画面の設定が間違っている可能性があるとのことで、その設定方法まで教えて頂きました。
これがダメだった時のスケッチ例の2_spi_settingの表示ですが
らびあんさんのご指導で正常な表示に
これで、画像表示をしてみると(この時点では赤と青の表示が反対になっています^^;)
おおっ!!!!念願のESP32での画像表示が!!!!!!
結構この表示が出来るまで時間がかかったのですが、らびあんさんは懇切丁寧に私のような初心者を最後まで面倒見てくださいました(><)ホントなんていい人なんだ・・・・!!
もう死ぬほどありがとうございますm(_ _)m
一度は諦めたESP32での画像表示ようやく達成できました!!!長かった~^^;
てなことで、同じようにやってみたいと思われる方用にlovyanGFXの使い方の説明を少し^^;
まずは、arduino IDEのライブラリーの管理でlovyaGFXを検索してインストール。
で、使うLCDに合わせて設定を行わないといけません。
ファイル→スケッチ例→LovyanGFX→HowToUse→2_spi_settingでこれを自分の使用したい環境に合わせて変更し、その設定部分を他のスケッチ例にコピペして使います。
今回私の場合は
ST7735 80x160の液晶
VSPI使用で
SCK=18(ピン番号)
SDA=23
CS=14
DC=27
RESET=33
BLK=32
という設定で使用します。
実際にスケッチ例のCollisionCirclesにコピペしたものがこちら
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 |
#include <LovyanGFX.hpp> struct LGFX_Config { static constexpr spi_host_device_t spi_host = VSPI_HOST; static constexpr int dma_channel = 1; static constexpr int spi_sclk = 18; static constexpr int spi_mosi = 23; static constexpr int spi_miso = -1; static constexpr int spi_dlen = 8; }; static lgfx::LGFX_SPI<LGFX_Config> lcd; static lgfx::Panel_ST7735S panel; struct ball_info_t { int32_t x; int32_t y; int32_t dx; int32_t dy; int32_t r; int32_t m; uint32_t color; }; static constexpr std::uint32_t SHIFTSIZE = 8; static constexpr std::uint32_t BALL_MAX = 256; //static LGFX lcd; static LGFX_Sprite _sprites[2]; static ball_info_t _balls[2][BALL_MAX]; static std::uint32_t _ball_count = 0, _fps = 0; static std::uint32_t ball_count = 0; static std::uint32_t sec, psec; static std::uint32_t fps = 0, frame_count = 0; static std::uint32_t _width; static std::uint32_t _height; volatile bool _is_running; volatile std::uint32_t _draw_count; volatile std::uint32_t _loop_count; static void drawfunc(void) { ball_info_t *balls; ball_info_t *a; LGFX_Sprite *sprite; auto width = _sprites[0].width(); auto height = _sprites[0].height(); std::size_t flip = _draw_count & 1; balls = &_balls[flip][0]; sprite = &(_sprites[flip]); sprite->clear(); for (int32_t i = 8; i < width; i += 16) { sprite->drawFastVLine(i, 0, height, 0x1F); } for (int32_t i = 8; i < height; i += 16) { sprite->drawFastHLine(0, i, width, 0x1F); } for (std::uint32_t i = 0; i < _ball_count; i++) { a = &balls[i]; sprite->fillCircle( a->x >> SHIFTSIZE , a->y >> SHIFTSIZE , a->r >> SHIFTSIZE , a->color); } sprite->setCursor(1,1); sprite->setTextColor(TFT_BLACK); sprite->printf("obj:%d fps:%d", _ball_count, _fps); sprite->setCursor(0,0); sprite->setTextColor(TFT_WHITE); sprite->printf("obj:%d fps:%d", _ball_count, _fps); union { std::uint32_t* s32; std::uint8_t* s; }; union { std::uint32_t* p32; std::uint8_t* p; }; s32 = (std::uint32_t*)sprite->getBuffer(); p32 = (std::uint32_t*)_sprites[!flip].getBuffer(); auto w32 = (width+3) >> 2; std::int32_t y = 0; do { std::int32_t x32 = 0; do { while (s32[x32] == p32[x32] && ++x32 < w32); if (x32 == w32) break; std::int32_t xs = x32 << 2; while (s[xs] == p[xs]) ++xs; while (++x32 < w32 && s32[x32] != p32[x32]); std::int32_t xe = (x32 << 2) - 1; if (xe >= width) xe = width - 1; while (s[xe] == p[xe]) --xe; lcd.pushImage(xs, y, xe - xs + 1, 1, &s[xs]); } while (x32 < w32); s32 += w32; p32 += w32; } while (++y < height); ++_draw_count; } static void mainfunc(void) { static constexpr float e = 0.999; // Coefficient of friction sec = millis() / 1000; if (psec != sec) { psec = sec; fps = frame_count; frame_count = 0; if (++ball_count >= BALL_MAX) { ball_count = 1; } auto a = &_balls[_loop_count & 1][ball_count - 1]; a->color = lgfx::color888(100+random(155), 100+random(155), 100+random(155)); a->x = 0; a->y = 0; a->dx = random(1, 3 << SHIFTSIZE); a->dy = random(1, 3 << SHIFTSIZE); a->r = (4 + (ball_count & 0x07)) << SHIFTSIZE; a->m = 4 + (ball_count & 0x07); } frame_count++; _loop_count++; ball_info_t *a, *b, *balls; int32_t rr, len, vx2vy2; float vx, vy, distance, t; size_t f = _loop_count & 1; balls = a = &_balls[f][0]; b = &_balls[!f][0]; memcpy(a, b, sizeof(ball_info_t) * ball_count); for (int i = 0; i != ball_count; i++) { a = &balls[i]; // a->dy += 4; // gravity a->x += a->dx; if (a->x < a->r) { a->x = a->r; if (a->dx < 0) a->dx = - a->dx*e; } else if (a->x >= _width - a->r) { a->x = _width - a->r -1; if (a->dx > 0) a->dx = - a->dx*e; } a->y += a->dy; if (a->y < a->r) { a->y = a->r; if (a->dy < 0) a->dy = - a->dy*e; } else if (a->y >= _height - a->r) { a->y = _height - a->r -1; if (a->dy > 0) a->dy = - a->dy*e; } for (int j = i + 1; j != ball_count; j++) { b = &balls[j]; rr = a->r + b->r; vx = a->x - b->x; if (abs(vx) > rr) continue; vy = a->y - b->y; if (abs(vy) > rr) continue; len = sqrt(vx * vx + vy * vy); if (len >= rr) continue; if (len == 0.0) continue; distance = (rr - len) >> 1; vx *= distance / len; vy *= distance / len; a->x += vx; b->x -= vx; vx = b->x - a->x; a->y += vy; b->y -= vy; vy = b->y - a->y; vx2vy2 = vx * vx + vy * vy; t = -(vx * a->dx + vy * a->dy) / vx2vy2; float arx = a->dx + vx * t; float ary = a->dy + vy * t; t = -(-vy * a->dx + vx * a->dy) / vx2vy2; float amx = a->dx - vy * t; float amy = a->dy + vx * t; t = -(vx * b->dx + vy * b->dy) / vx2vy2; float brx = b->dx + vx * t; float bry = b->dy + vy * t; t = -(-vy * b->dx + vx * b->dy) / vx2vy2; float bmx = b->dx - vy * t; float bmy = b->dy + vx * t; float adx = (a->m * amx + b->m * bmx + bmx * e * b->m - amx * e * b->m) / (a->m + b->m); float bdx = - e * (bmx - amx) + adx; float ady = (a->m * amy + b->m * bmy + bmy * e * b->m - amy * e * b->m) / (a->m + b->m); float bdy = - e * (bmy - amy) + ady; a->dx = round(adx + arx); a->dy = round(ady + ary); b->dx = round(bdx + brx); b->dy = round(bdy + bry); } } _fps = fps; _ball_count = ball_count; } #if defined (ESP32) || defined (CONFIG_IDF_TARGET_ESP32) || defined (ESP_PLATFORM) static void taskDraw(void*) { while ( _is_running ) { while (_loop_count == _draw_count) { taskYIELD(); } drawfunc(); } vTaskDelete(NULL); } #endif void setup(void) {panel.freq_write = 20000000; panel.freq_fill = 27000000; panel.freq_read = 16000000; panel.spi_mode = 0; panel.spi_mode_read = 0; panel.len_dummy_read_pixel = 8; panel.spi_read = true; panel.spi_3wire = false; panel.spi_cs = 14; panel.spi_dc = 27; panel.gpio_rst = 33; panel.gpio_bl = 32; panel.pwm_ch_bl = 7; panel.backlight_level = true; panel.invert = true; panel.reverse_invert = true; panel.rgb_order = false; panel.memory_width = 128; panel.memory_height = 160; panel.panel_width = 80; panel.panel_height = 160; panel.offset_x =24; panel.offset_y = 0; panel.rotation = 0; panel.offset_rotation = 0; lcd.setPanel(&panel); lcd.begin(); lcd.startWrite(); if (lcd.width() < lcd.height()) lcd.setRotation(lcd.getRotation() ^ 1); auto lcd_width = lcd.width(); auto lcd_height = lcd.height(); for (std::uint32_t i = 0; i < 2; ++i) { _sprites[i].setTextSize(2); _sprites[i].setColorDepth(8); } bool fail = false; for (std::uint32_t i = 0; !fail && i < 2; ++i) { fail = !_sprites[i].createSprite(lcd_width, lcd_height); } if (fail) { fail = false; for (std::uint32_t i = 0; !fail && i < 2; ++i) { _sprites[i].setPsram(true); fail = !_sprites[i].createSprite(lcd_width, lcd_height); } if (fail) { fail = false; if (lcd_width > 320) lcd_width = 320; if (lcd_height > 240) lcd_height = 240; for (std::uint32_t i = 0; !fail && i < 2; ++i) { _sprites[i].setPsram(true); fail = !_sprites[i].createSprite(lcd_width, lcd_height); } if (fail) { lcd.print("createSprite fail..."); delay(3000); } } } _width = lcd_width << SHIFTSIZE; _height = lcd_height << SHIFTSIZE; for (std::uint32_t i = 0; i < ball_count; ++i) { auto a = &_balls[_loop_count & 1][i]; a->color = lgfx::color888(100+random(155), 100+random(155), 100+random(155)); a->x = 0; a->y = 0; a->dx = random(1, 3 << SHIFTSIZE); a->dy = random(1, 3 << SHIFTSIZE); a->r = (4 + (i & 0x07)) << SHIFTSIZE; a->m = 4 + (i & 0x07); } _is_running = true; _draw_count = 0; _loop_count = 0; #if defined (ESP32) || defined (CONFIG_IDF_TARGET_ESP32) || defined (ESP_PLATFORM) disableCore0WDT(); xTaskCreate(taskDraw, "taskDraw", 2048, NULL, 0, NULL); #endif } void loop(void) { mainfunc(); #if defined (ESP32) || defined (CONFIG_IDF_TARGET_ESP32) || defined (ESP_PLATFORM) while (_loop_count != _draw_count) taskYIELD(); #else drawfunc(); #endif } |
#include <LovyanGFX.hpp>(1行目)の後ろに
2行目のstruct LGFX_Configから17行目のstatic lgfx::Panel_ST7735S panel;までを
張り付け。
さらにsetup内の
249行目から286行目までを張り付けています。
この2つのパート部分がST7735を80×160で使うための設定です。
それぞれの設定値の意味については2_spi_settingを見てください。
このほかのSpriteのスケッチ例も上記の部分を張り付けて設定しなおせば動かすことが出来ます。
初めはこの設定が分からなくて、らびやんさんにご指導いただいた部分です。
ちなみに後からアリエクスプレスで購入した同じ液晶なのですが、少し設定を変えないと色や表示位置がおかしい物が有りました。そんな時は下のように変更してください。
setup内に張り付けた部分の一部ですが、
panel.invert = true;
panel.reverse_invert = true;
panel.rgb_order = false;
panel.memory_width = 128;
panel.memory_height = 160;
panel.panel_width = 80;
panel.panel_height = 160;
panel.offset_x =24;
panel.offset_y = 0;
を
panel.invert = false;
panel.reverse_invert = true;
panel.rgb_order = false;
panel.memory_width = 132;
panel.memory_height = 162;
panel.panel_width = 80;
panel.panel_height = 160;
panel.offset_x =26;
panel.offset_y = 1;
に修正です。
これで正常に表示されています^^
で、これでSpriteのどのスケッチ例も動かせるようになったのですが、これらのスケッチ例を見ていると画像が結構グリングリン動いているんですよね~。
そもそもspriteて何????てな初心者ですからネットにドボンでw
こちらのサイトで的確に説明されておられて、スクロールについてまで勉強出来ました。
なるほど~!そういう事か!!
よし!!これで基本は出来たぞ~~!てなことでまた最初から少しずつ製作していきます。たとえば、ミサイル迎撃画面。
まずはサブモニターにスプライトで作っておいた画面を縮小して貼り付け、メイン画面の枠を決めます。
次に味方マークの三角を描画。
編隊になるように数を増やして
この時急遽上に文字とか表示する枠を作ったので最初の三角も移動
てな感じで少しづつ描画しては確認を繰り返していたのですが、最後ごろになると
これスプライトのプログラムですが、このように数字の羅列だけで
こんな感じになるだろうなというのが大体わかるようになりましたww
こうなると描画はスピードアップですwモニターとか無い病院の空き時間でもプログラム書けるようになって、家に帰ってモニターで確認してもそんなに大きな間違いしなくなりましたw
人間慣れってこわいww
とにかくプログラムが長くなり過ぎて、テストなども面倒になってきたので、
別ファイルでシーンごとにプログラム開発してテスト確認後、それを本編のプログラムにコピペして再度全体の流れをテストするという方法に^^
基本的な流れは前回のOLEDで分かっていましたので、前回の物では表現できなかったミサイルの軌跡とかも入れてみました。やはりマクロスと言えばあのミサイルですもんね~w
で、なんとかプログラムがあがったのが月曜日。その土曜日にはオンラインドロオフでのお披露目予定。
てなことで躊躇する間もなく前回のOLEDバージョンを取り外しw
どりゃ~~~!もったいないけどポイw
さらにモニター部分が変わったのでコンソールのモデリング修正
コンソールとコンソールカバーをリモデリング。
火曜日終了!
次の日、病院で3Dプリントして塗装。
TFT液晶の方も仕込むために出来るだけ小さく
ここまで基板を削れましたが、この後この液晶さんはお亡くなりになられました・・・・・・w
実はこれで液晶の交換の必要性が出て交換したら上記のように設定の一部を変えないといけない状況になり、その設定の変更にまた時間を取られてしまいましたw
あかんwそんなことやっている場合ではないwとりあえず削らなくても何とか入ることが分かりましたので、ドロオフ用にはそのままで行こうwあとでまた吟味しよう^^;
次にESP32の方も全部足ついているから内蔵には邪魔なので足部分のハンダを溶かして除去!
あれwESP32の基板からひげが生えた!ちょww基板のプリント部分が一部剥がれたwwww
強引に足を取りすぎww
でも大丈夫丁度その端子使わないwおひげは邪魔なのでカットさせていただきました。
水曜日終了!
おしゃ!とりあえずこれで全部仕込んでしまえ!
えっと、電源は、、、、、、5V端子に5V入力でもオッケー!それで行きます!
うし!何とか形になった!
次はフィギュアも塗装しておかねば!!
組み立ててみると・・・・・・・ぎゃ~~~!!ヘルメットパーツの赤い部分を一部無くしてるww
やべ~!間に合わない・・・・・終わったw
・・・・・・・・・・・・・あっw反対側から撮影すればいいのかwwパニクッてたw
よしw右のパーツ無い所みえないww
木曜日終了!
次は動画撮影だ~!
これまた光の具合や液晶の見え方等が気に入らずに何度も撮影して動画編集。
サウンドとどうしても合わない部分はプログラムの方の時間を調整して再度書き込み撮影しなおしwやべーよ終わらないよ~~~~!!
金曜日終了!
やば土曜日ドロオフオンラインの日が来ちゃったよ!!!
午前中は仕事なので午後からが勝負w
昼食も抜きで速攻で家に帰り、youtube用とtwitter用の動画を編集。でもyoutube用がどうしても気に入らずに結局金曜日に作ったもので行くことに。
twitter用は2分20秒に纏めないといけない・・・・・・ムリwwもういいやwとにかく削っちゃえww
てなことで電飾タイムの4時にギリギリ間に合いましたww
いや~ホント疲れたww
その分良い評価いただけたし、オンライン懇親会でも憧れのモデラーさん達にも褒めて頂けてホント嬉しかったです^^
ちなみに今回モニター表示に使った画像は動画やネットから集めた物を加工したので著作権の関係で再配布というわけにはいかないので、プログラムだけ上げてもあまり意味がないかもです^^;
それでも、アホなプログラム見てみたいという希望が有れば後日掲載します。ちなみに5000行越えですw
関連すると思われる記事:
- None Found
ふんふんふむふむ。
すやすや。ぴぃぴぃ。ぐご~~。
はっ!
よくもまあこんなことまでごいごいすーでございます。
なんとなくラズパイとにたとこもあるかな?
これはこれですごいですけど、動画パワーはラズパイもなかなか魅力的ですよ!
と、また沼に引きずり込んでみる。
ふふふ。
てつのさん こんばんは!
ラズパイですよね~!
昨年ちょっとかじってみて美味しいのはわかったのですが、ラズパイでのCのプログラミング方法とかまだよくわからずにそのまま投げてありますw
zeeoとかなら何とか模型に組み込めそうですが、、、、
パワーではラズパイの方が上なんでしょうね~。
そちらの勉強に振り分ける時間が見つかったら試してみます^^
こんばんは!
ドロオフお披露目までの工程はなかなかのドラマですね(^^;
スクラッチからコピペがあまり効かなそうなプログラムを1週間程度でしょうか?で5000行ってなかなか凄まじいペースです。しかもこの完成度。
相変わらず当然のようにバズってたしさすがですね~♪
なおさん こんばんは!
ドロオフお披露目間に合ってよかったですよww
ホント動画作成時の音合わせがなかなかうまくいかずにプログラムも何回か書き換えました^^;
プログラムは場面ごとに変わるのでなかなかコピペが効かないのですが、Spriteを使うことによってそれを移動させたり回転させたりしていますのでその辺は少しプログラムは楽でした。
ただ、それでも最後のドックファイト部分は100ms単位で画面の表示プログラム書いていますw
twitterの皆さんはコクピット好きなんですよね~w今回も沢山評価いただけて良かったです^^
こんばんは!
どひゃ~!!!
動画がますますパワーアップしていますね、怒涛の様な一週間の作業が目に浮かびます。
まずは、本当にお疲れさまでした。こんなに多忙な時に、いろいろアホな質問して、6jiroさんの大切な工作時間を無駄にさせて、申し訳ありませんでした。
動画と音声をあわせて楽しませていただきました。もう、ニヤニヤしながら、「そーくるか~!」「お~」なんて言いながら、存分に雰囲気に浸れました。
LovyanGFXは凄いですね。
わたしはこれから6jiroさんの後を追いかけていきますので、よろしくお願いします!!
Vividさん こんばんは!
最終的にこのような動画になりました^^
ホント最後は怒涛の1週間でしたw
いえいえw Vividさんの質問はレベルが高すぎて答えられないことがほとんどですみませんw
LovyanGFX最強ですよね~。あの高速描画とsprite機能はホント無限の可能性が広がりますよ!
多分あと少ししたら、私がVividさんの背中を追いかける構造になるのは間違いありませんw
スプライト使いこなすって、あとは当たり判定と入力つければゲームじゃないすかw
いやー、直前までの追い込みの集中ぶりと仕事量はさすがです。
今回は4jiro~6jiroまで出たのではないでしょうか?
基盤削って没にしたり、メータープリントしなおしたり、パーツなくしたり、、、
ブログネタ5回分くらいの容量を一気に駆け抜けましたねwww
今回のプログラム量も半端ないですが、5000行って、、、
物には限度ってものがあるでしょうにww
ちなみに、私だったらめんどーくさいのでPCで動画作ってそれを小画角の動画に変換して、たらたらと再生して終わりにしますねw
今回はちゃんと内部で処理してるんで、入力に対応させれば色々インタラクティブに動かせるんじゃないでしょうか。
可能性広がります。
gyoさん こんばんは!
うむ・・・・・・確かにゲームにする方法が思いつくww
とにかくまたなんだかんだで色々やりましたよ~!
うっかりも、もうあの位ではビクともしませんからねw
ちょっとパーツ無くした時はパニクリましたがww
プログラムの約半分以上が最後のドッグファイトで費やされていますw
あそこで3000行近く費やしていますからね^^;
動画にして変換する方法も考えたのですが、80x64ドットでしょう、通常の絵なんてこのサイズに縮小すると何が書いてあるかわからなくなるんですよねww
離陸の時のバックの絵とか何度加工しなおしたかww
ちなみに、アシェットの方にも仕込めるか検討してフォッカーさんバージョンも作らないといけないかなと思案中ですw
本体はよ作れ!との声も聞こえてきますがww