📃Programming: Arduboy
https://gyazo.com/28c12eb761e2941a39e942897dd6eeba
Arduboyを使用した24fpsのAnimate風ストップウォッチの自作記録
ソースコード
code:c
/**
* @file AdobeAnimateStopWatch.ino
* @brief アニメーターのタイミング取り用のストップウォッチプログラム
* @author maxcaffy
* @date 19/08/23
* @detail B: 再生、再生中のキーフレームのマーク / A:再生or停止 / 上:キーフレームのマーク / 左右:現在のフレーム位置移動 / 下:リセット and FPS切り替え / 上を押しながら下で音のON/OFF切り替え
* @version 1.1 音量調整をつけた / 1秒ごとの音をけした
* @version 1.2 音量調整キーを変更(左右キー同時押し) / デフォルトOFF / 1秒ごとの光をけした / 音のタイミング調整 / ねこの位置調整
*/
Arduboy2 arduboy; // create class
Tinyfont tinyfont = Tinyfont(arduboy.sBuffer, Arduboy2::width(), Arduboy2::height());
// TINYFONTの固定サイズ 実際には1pxのスペーシングがあるため5
// Animate風フレーム設定
// フレームの状態 空白 or キーフレーム or 現在位置(結局使ってない)
#define FRAME_NUM_OFFSET 1 // Animateは1始まりなので、フレーム数に1を足す用 // フレームレート設定
#define RATE_MAX_MAX 150 // より多いフレームレートで使われる最大の数を入れる(配列宣言用) // ねこをおどらせるための定数&変数
// LEDの強さ 最大にするとまぶしすぎ
// ねことアイコンの位置
// ねこのアニメーションデータ
const unsigned char PROGMEM catDanceSprite[] =
{
// width, height,
16, 16,
// FRAME 00
0x00, 0x00, 0x00, 0x7c, 0xfe, 0xd4, 0xbc, 0xdc, 0xbc, 0xd4, 0xfe, 0x3c, 0x80, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x36, 0x37, 0x3b, 0x3f, 0x3f, 0x3f, 0x3f, 0x7f, 0x73, 0x01, 0x01, 0x00, 0x00,
// FRAME 01
0x00, 0x00, 0x00, 0x78, 0xfc, 0xa8, 0x78, 0xb8, 0x78, 0xa8, 0xfc, 0x78, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x63, 0x7a, 0x3f, 0x1f, 0x1f, 0x1f, 0x1f, 0x7f, 0x7f, 0x03, 0x03, 0x00, 0x00,
// FRAME 02
0x00, 0x00, 0x00, 0x00, 0xf8, 0xfc, 0xa8, 0x78, 0xb8, 0x78, 0xa8, 0xfc, 0xf8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x03, 0x62, 0x7f, 0x3f, 0x1f, 0x1f, 0x1f, 0x1f, 0x3d, 0x76, 0x66, 0x00, 0x00,
// FRAME 03
0x00, 0x00, 0x00, 0x80, 0x80, 0x3c, 0xfe, 0xd4, 0xbc, 0xdc, 0xbc, 0xd4, 0xfe, 0x7c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x01, 0x73, 0x7f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3b, 0x37, 0x36, 0x00, 0x00,
// FRAME 04
0x00, 0x00, 0x00, 0x00, 0x78, 0xfc, 0xa8, 0x78, 0xb8, 0x78, 0xa8, 0xfc, 0x78, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x03, 0x7f, 0x7f, 0x1f, 0x1f, 0x1f, 0x1f, 0x3f, 0x7a, 0x63, 0x03, 0x00, 0x00,
// FRAME 05
0x40, 0xf0, 0x70, 0xb8, 0x58, 0xb8, 0x00, 0xf8, 0x00, 0xa8, 0x54, 0xfc, 0x64, 0xf8, 0xf8, 0x10,
0x55, 0x52, 0x55, 0x52, 0x55, 0x52, 0x50, 0x5f, 0x60, 0x53, 0x57, 0x57, 0x56, 0x57, 0x6b, 0x61,
};
byte catCurrentF = 0;
// 再生停止マークデータ
const unsigned char PROGMEM playIconSprite[] =
{
// width, height,
16, 16,
// FRAME 00
0x00, 0x00, 0x00, 0x00, 0xfe, 0xfc, 0xfc, 0xf8, 0xf0, 0xf0, 0xe0, 0xc0, 0xc0, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x3f, 0x1f, 0x1f, 0x0f, 0x07, 0x07, 0x03, 0x01, 0x01, 0x00, 0x00, 0x00,
// FRAME 01
0x00, 0x00, 0x00, 0xfc, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xfc, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x1f, 0x00, 0x00, 0x00,
};
// フレームレート設定
const byte F_RATE_NUM = 2;
int currentR = RATE_TYPE_24;
// 現在のフレーム位置とか
int currentF = 0;
// 再生中フラグ
bool isRun = false;
// 全フレームの状態配列
// LED関連
enum class Color {
RED,
GREEN,
BLUE,
COUNT
};
// Analog LED values
byte digitalState3 = { RGB_OFF, RGB_OFF, RGB_OFF }; byte analogValue3 = { 0, 0, 0}; // ブザー用
BeepPin1 beep;
bool isBeep = false; // デフォルトOFF
void setup() {
arduboy.begin();
arduboy.setFrameRate(F_RATEScurrentR); //default 60 //if(isBeep) {
beep.begin(); // Set up the hardware for playing tones
//}
for (byte i = 0; i < RATE_MAX_MAX; i++){
arrayFi = FRAME_TYPE_BLANK; }
analogSet();
}
void loop() {
// 次のフレームに入るまでレンダリングを停止する ------------------------------------------------------------
if (!(arduboy.nextFrame())) {
return;
}
beep.timer(); // handle tone duration
arduboy.pollButtons(); // justPressedを使用するときは必須
if (arduboy.justPressed(B_BUTTON)) { // 押された瞬間を取得
if(isBeep) {
beep.tone(beep.freq(440), 1); // 推した瞬間ブザーを鳴らす
}
// 停止状態なら再生
if (!isRun) {
isRun = true;
}
}
if (arduboy.justPressed(A_BUTTON)) { // 押された瞬間を取得
// 時間の一時停止・再生
isRun = !isRun;
}
if (arduboy.justPressed(LEFT_BUTTON)) { // 押された瞬間を取得
isRun = false;
currentF --;
if (currentF < 0) {
}
}
if (arduboy.justPressed(RIGHT_BUTTON)) { // 押された瞬間を取得
isRun = false;
currentF ++;
currentF = 0;
}
}
if (arduboy.justPressed(UP_BUTTON)) { // 押された瞬間を取得
isRun = false;
} else {
}
}
if (arduboy.justPressed(DOWN_BUTTON)) { // 押された瞬間を取得
if (currentR == RATE_TYPE_24) {
currentR = RATE_TYPE_30;
} else {
currentR = RATE_TYPE_24;
}
reset();
}
// 左を押しながら右を押して音のON/OFF切り替え
if (arduboy.pressed(LEFT_BUTTON) && arduboy.justPressed(RIGHT_BUTTON)) {
isBeep = !isBeep;
if(isBeep){
beep.tone(beep.freq(1100), 1); // ピ↑
} else {
beep.tone(beep.freq(220), 1); // ピ↓
}
}
if (isRun){
// 一番最初のフレームは必ず音を鳴らす
if (currentF == 0 && isBeep) {
beep.tone(beep.freq(990), 1); // プ
}
currentF ++;
currentF = 0;
isRun = false;
}
}
// フレーム情報を初期化
for (byte i = 0; i < F_MAXcurrentR; i++){ if (arrayFi != FRAME_TYPE_KEY) { arrayFi = FRAME_TYPE_BLANK; }
}
}
arduboy.clear();
// 現在のフレームレートを右下に描画
tinyfont.setCursor(WIDTH - (TINYFONT_SIZE + 1) * 5, HEIGHT - TINYFONT_SIZE);
tinyfont.setCursor(WIDTH - (TINYFONT_SIZE + 1) * 3, HEIGHT - TINYFONT_SIZE);
tinyfont.print("FPS");
// 音ON/OFFアイコン
if(isBeep){
tinyfont.setCursor(0, HEIGHT - TINYFONT_SIZE);
tinyfont.print("d");
}
for (byte k = 0; k < row; k ++) {
for (byte i = 0; i < F_RATEScurrentR; i ++) { // フレームを描画
drawAnFrame(i * (ANFRAME_W - 1), k * (ANFRAME_H + ANFRAME_GAP), arrayF[k * F_RATEScurrentR + i], k * F_RATEScurrentR + i); }
}
// 現在のフレームを表す.を描画
arduboy.drawPixel(px * (ANFRAME_W - 1) + ANFRAME_HW, py * (ANFRAME_H + ANFRAME_GAP) + ANFRAME_H, WHITE);
// LEDをぴかぴかさせたい
if (isRun) {
// // 1秒おきに光らせる
// if (!(currentF % (F_RATEScurrentR))){ // if(isBeep) {
// // beep.tone(beep.freq(880), 1); // ピ
// }
// }
// キーフレームの存在するところを光らせる
if(isBeep) {
beep.tone(beep.freq(660), 1); // プ
}
}
} else {
// 再生終了時に赤色に光らせる
if (currentF == 0) {
}
}
analogSet();
// 猫ダンスと再生アイコン 場所はてきとう 24fpsモードのみ出現
if (currentR == RATE_TYPE_24){
// 再生中は踊り、停止すると寝る
if (isRun){
Sprites::drawSelfMasked(ICON_X, ICON_Y, playIconSprite, ICON_PLAY);
Sprites::drawSelfMasked(CAT_X, CAT_Y, catDanceSprite, catCurrentF);
catCurrentF ++;
if(catCurrentF > CAT_FRAME_NUM){
catCurrentF = 0;
}
} else {
Sprites::drawSelfMasked(ICON_X, ICON_Y, playIconSprite, ICON_STOP);
Sprites::drawSelfMasked(CAT_X, CAT_Y, catDanceSprite, CAT_FRAME_SLEEP);
}
}
// 更新処理
arduboy.display();
}
/**
* @fn
* Animate風のフレームを画面に描画する
* @brief Animate風のフレームを画面に描画する
* @param x 描画座標(左上)
* @param y 描画座標(左上)
* @param type タイプ切り替え キーフレームか空白のフレームか現在のフレームか
* @frame 現在のフレーム番号
* @return なし
*/
void drawAnFrame(byte x, byte y, byte type, byte frame) {
if (type == FRAME_TYPE_CURRENT){
arduboy.drawRect(x, y, ANFRAME_W, ANFRAME_H, WHITE);
//arduboy.drawPixel(x + ANFRAME_HW, y + ANFRAME_H, WHITE);
} else if (type == FRAME_TYPE_BLANK) {
arduboy.drawRect(x, y, ANFRAME_W, ANFRAME_H, WHITE);
} else {
arduboy.fillRect(x, y, ANFRAME_W, ANFRAME_H, WHITE);
arduboy.fillCircle(x + ANFRAME_HW, y + ANFRAME_HH, 1, BLACK);
arduboy.fillRect(x-1, y + ANFRAME_H, (TINYFONT_SIZE + 1) * getDigit(frame)+2, TINYFONT_SIZE + 2, BLACK);
tinyfont.setCursor(x, y + ANFRAME_H + 1);
tinyfont.print(frame + FRAME_NUM_OFFSET); // Animateは1始まりなので数値を足す
}
}
/**
* @fn
* 10進法の桁数を返す
* @brief 10進法の桁数を返す
* @param num 桁数を調べる数値
* @return 桁数
*/
unsigned getDigit(byte num){
byte digit=0;
while(num!=0){
num /= 10;
digit++;
}
return digit;
}
/**
* @fn
* リセット
* @brief 見た目をリセットする。正確なりセットではない
* @return なし
*/
void reset(){
currentF = 0;
isRun = false;
for (byte i = 0; i < RATE_MAX_MAX; i++){
arrayFi = FRAME_TYPE_BLANK; }
catCurrentF = 0;
arduboy.setFrameRate(F_RATEScurrentR); //default 60 analogSet();
}
/**
* @fn
* Update all LEDs in analog mode
* @brief Update all LEDs in analog mode
* @return なし
*/
void analogSet() {
}
参考資料
Arduino + IDE
お手軽開発基盤シリーズ
C言語で動く
RasPiよりも組み込み用途向け、OS不要、単機能、省電力、シンプル
専用エディタ有り
コンパイルから自動でUSB経由でArduinoに書き込み
ライブラリの自動インストール、更新、検索機能
クラウドエディタもあるが無料版は制限あり
Arduboy
Arduinoの一種
Arduino Leopardベースに各種スイッチ、LED、画面、バッテリーが接続されている
専用ライブラリ有り
Arduboy2.h
Arduboy.hと後方互換がある拡張版
Arduino 日本語リファレンス
Arduboy API Docs
各ライブラリに付属しているサンプルプログラム