inPulse watchでマンデルブロのジュリア集合で遊ぶ
積んどくだけの『高級積みARM基板』にならないようにちょっと遊んでいます。
定番のマンデルブロ集合演算です。C言語での開発で、inPulseのAPIも揃ってるので開発は容易ですが、つまづいた点を少し。
- タスクはOSが管理している
Pulse OSというOSが管理しています。結構時間にシビアです。5秒に1回、メインルーチンの
void main_app_loop()
にリターンする必要があります。サブルーチンで永延と(5秒以上)作業してるとWDTによりリセットされてしまいます。
サブルーチンも無駄なループを避けるとか、変数をstaticにするとか、これらを考慮した記述に書き換えます。
- RGBデータはcolor24_tという構造体に格納
なのでRGBそれぞれ8bitずつ。例えば赤を255にしたければ
color24_t * color;
color->red = 255;
みたいな感じ。あとはAPI(pulse_draw_point24())にこのポインタを投げるだけでOLEDに表示してくれる
pulse_draw_point24(bg_color);
フラクタル描画は以前MARY-DUINOで作った時のコードを書き換えています。
http://www45.atwiki.jp/marykiban/pages/51.html
■使い方(Mac OSX)
- inPulse SDKをまだDLしてないならここからダウンロード。解凍すると、[pulse_sdk]フォルダが出来るのでデスクトップとか、使いやすい任意の場所に移動する。
- このエントリの最後に貼付けてるソースコードをまるっとコピーして[pulse_app2.c]などどいう名前で保存。*1
- pulse_app2.cを pulse_sdk/src/にコピー。くわえて、pulse_sdkフォルダにTerminal.appで移動
$ cp examples/featured_apps/Psychedelic\ Clock/include/app_resources.h src/
app_resources.hはPsychedelic Clockのものを使用。
- MacとinPulse watchとのペアリング終わっているならL2CAPServerを起動する
$ java -d32 -jar ./tools/L2CAPServer/L2CAPServer.jar
- [command ⌘ ]+[T]で新しいウィンドウタブで
$ python compileandload.py -d 00:50:xx:xx:xx:xx
これでしばらく待つとコンパイルとデプロイが完了するはず
ぬーん
■ソースコード*2
/** * Mandelbrot's Julia set. (z_{n+1} = z_{n}^2 + C) **/ #include <pulse_os.h> #include <pulse_types.h> #include "app_resources.h" // This is a text box widget that will define the bounds of where to render the text struct PWTextBox text_box; // Text widget that stores required information about rendering the text // It must be initialized before it can be used struct PWidgetTextDynamic text_widget; // Text buffer to store text to be rendered char text_buffer[16]; volatile int count; volatile float mag; volatile float offsetx; volatile float offsety; color24_t bg_color = {255, 0, 0, 0}; void complex_mult(float ar,float ai,float br,float bi,float *cr,float *ci) { // C = A * B *cr = ar * br - ai * bi; *ci = ar * bi + ai * br; } float complex_abs2(float r,float i) { float ar,ai; complex_mult(r,i,r,-i,&ar,&ai); return ar; } void psychedlic_background() { //pulse_set_draw_window(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); // indicate drawing field static int x; static int y; //for(int y = 0; y < SCREEN_HEIGHT; y++) //{ pulse_set_draw_window(0, y, SCREEN_WIDTH - 1, y); // indicate drawing field for(x = 0; x <SCREEN_WIDTH ; x++) { float cr = (x - 64) / 64. / mag + offsetx; float ci = (y - 64) / 64. / mag + offsety; float zr = 0; float zi = 0; int t; for(t = 0; t < 512; t++) { if(complex_abs2(zr,zi) > 4) break; complex_mult(zr,zi,zr,zi,&zr,&zi); zr += cr; zi += ci; } cycle_color(&bg_color, t, 64); pulse_draw_point24(bg_color); } y++; if(y >= SCREEN_HEIGHT) { y = 0; mag = mag * 1.2; if(count++ > 100) { offsetx = -0.756423894274328; offsety = 0.064179410646170; mag = 1; count = 0; } } } void cycle_color(color24_t * color, int count, int base) { //int r,g,b; int d = (count % base) * 256 / base; int m = (int)(d / 42.667); switch(m) { case 0: color->red=0; color->green=6*d; color->blue=255; break; case 1: color->red=0; color->green=255; color->blue=255-6*(d-43);break; case 2: color->red=6*(d-86); color->green=255; color->blue=0; break; case 3: color->red=255; color->green=255-6*(d-129);color->blue=0; break; case 4: color->red=255; color->green=0; color->blue=6*(d-171); break; case 5: color->red=255-6*(d-214); color->green=0; color->blue=255; break; default: color->red=0; color->green=0; color->blue=0; break; } } void print_time_into_text_buffer() { struct pulse_time_tm current_time; pulse_get_time_date(¤t_time); uint32_t hour = current_time.tm_hour; if(hour == 0) { hour = 12; } else if(hour > 12) { hour -= 12; } sprintf(text_buffer, "%d:%02d", current_time.tm_hour, current_time.tm_min); } void draw_clock() { // Initialize the text box parameters text_box.top = 10; text_box.right = SCREEN_WIDTH; text_box.left = 0; text_box.bottom = SCREEN_HEIGHT; // Configure the text style // We want it to be centered both vertically and horizontally in the text box // and to truncate any text if it won't fit in the text box enum PWTextStyle style = (PWTS_TRUNCATE | PWTS_CENTER | PWTS_VERTICAL_CENTER); // Initialize the text widget pulse_init_dynamic_text_widget(&text_widget, text_buffer, FONT_CLOCKOPIA_32, COLOR_BLACK24, style); // Print the time from the RTC (real time clock) in to the text buffer print_time_into_text_buffer(); text_widget.font.resource_id = FONT_CLOCKOPIA_32; // Set the font color to be psuedo-random text_widget.font.color = COLOR_BLACK24; // Render the text in the text box area pulse_render_text(&text_box, &text_widget); } // This function is called once after the watch has booted up // and the OS has loaded void main_app_init() { count = 0; mag = 1; offsetx = -0.756423894274328; offsety = 0.064179410646170; } void main_app_handle_button_down() { pulse_update_power_down_timer(100); } void main_app_handle_button_up() { } // Main loop. This function is called frequently. // No blocking calls are allowed in this function or else the watch will reset. // The inPulse watchdog timer will kick in after 5 seconds if a blocking // call is made. void main_app_loop() { psychedlic_background(); //draw_clock(); } // This function is called whenever the processor is about to sleep (to conserve power) // The sleep functionality is scheduled with pulse_update_power_down_timer(uint32_t) void main_app_handle_doz() { } void main_app_handle_hardware_update(enum PulseHardwareEvent event) { }