因為最近開始玩Helldivers 2遊戲, 裡面呼叫武器與支援有很多需要數個上下左右組合按鈕去呼叫. 所以便萌生了自己製作的想法.
![](https://www.soujirou.info/blog/wp-content/uploads/2024/04/27_01.jpg)
路過彰化銀行剛好看到招牌被拆下來
![](https://www.soujirou.info/blog/wp-content/uploads/2024/04/27_02.jpg)
這個還蠻好喝的
![](https://www.soujirou.info/blog/wp-content/uploads/2024/04/27_04.jpg)
![](https://www.soujirou.info/blog/wp-content/uploads/2024/04/27_05.jpg)
這邊好特別!
![](https://www.soujirou.info/blog/wp-content/uploads/2024/04/27_06.jpg)
![](https://www.soujirou.info/blog/wp-content/uploads/2024/04/27_07.jpg)
邊吃飯邊看美食動畫XD
![](https://www.soujirou.info/blog/wp-content/uploads/2024/04/27_11.jpg)
電路圖先畫好
MCU用ATtiny85. 一片大概兩百不到, 超便宜!
先大概放一下感覺一下
然後就照著電路圖去焊電路啦~
![](https://www.soujirou.info/blog/wp-content/uploads/2024/04/27_08.jpg)
![](https://www.soujirou.info/blog/wp-content/uploads/2024/04/27_09.jpg)
![](https://www.soujirou.info/blog/wp-content/uploads/2024/04/27_10.jpg)
用0603的電阻比較省空間.
剩下的就是用Arduino IDE去把程式寫好
ATtiny85 board
https://projecthub.arduino.cc/alaspuresujay/use-an-attiny85-with-arduino-ide-d847c5
加板子, Arduino IDE設定裡加入這個網址
https://raw.githubusercontent.com/digistump/arduino-boards-index/master/package_digistump_index.json
去Borad manager找到Digistump AVR Boards 選安裝
下載驅動程式
https://github.com/LilyGO/DigiSpark-ATtiny85-driver-install
ATtiny85的板子要按下Arduino裡IDE的Upload再插板子才會燒code
完整程式碼
#include "DigiKeyboard.h" //For Digispark board
#define DEBOUNCE_DELAY 30
#define DELAY1 350
#define DELAY2 200
#define DELAY3 1000
#define MAX_RANDOM_DELAY 50
#define MAX_COMMAND_KEY_NUM 10
enum BT_NUM {
BT_NC = 0,
BT_1 = 1,
BT_2,
BT_3,
BT_4,
BT_5,
BT_6,
BT_7,
BT_8,
BT_9,
BT_10,
BT_NA,
};
//Hardware setting
//define pin
//const int pin0 = 0;
const int LED_R = 1; //Blink the LED during send key
//const int pin2 = 2;
//int pin3 = 3; //USB D+
//int pin4 = 4; //USB D-
//int pin5 = 5; //Unstable don't use
//other HW settings
const int BT_VOLT_PIN = A1; //Analog Button in one input //P2
const float GPIO_VOL = 4.8; //GPIO volt (3.3V or 5V)
const int TOTAL_BT_NUM = 10; //Total button number
const int R1 = 1000; //R1 ohm value
const int R2 = 470; //R2 ohm value
float BT_volt[TOTAL_BT_NUM + 1] = {0};
const float BT_volt_offset = 0.05;
//deboundce
BT_NUM last_read_BT = BT_NA;
BT_NUM last_BT = BT_NA;
unsigned long last_read_BT_time = 0;
const int deBounce_time = DEBOUNCE_DELAY; //ms
//按鈕串電路
//https://104.es/index.php/2018/05/28/arduino-unit-15/
//指令
//https://www.thegamer.com/helldivers-2-complete-stratagem-button-input-list/#how-to-change-keybinds-for-stratagem-inputs-on-pc
//https://forum.gamer.com.tw/C.php?bsn=24827&snA=1344
//任務
const byte keycmd_reinforce[MAX_COMMAND_KEY_NUM] = {KEY_W, KEY_S, KEY_D, KEY_A, KEY_W, 0}; //增援
const byte keycmd_resupply[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_S, KEY_W, KEY_D, 0}; //重新補給
//戰略配備
//愛國行政中心
const byte keycmd_machine_gun[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_A, KEY_S, KEY_W, KEY_D, 0}; //機槍
const byte keycmd_anti_Materiel_rifle[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_A, KEY_D, KEY_W, KEY_S, 0}; //反器材步槍
const byte keycmd_stalwart[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_A, KEY_S, KEY_W, KEY_W, KEY_A, 0}; //盟友
const byte keycmd_anti_tank[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_S, KEY_A, KEY_W, KEY_D, 0}; //反坦克消耗品
const byte keycmd_recoilless_rifle[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_A, KEY_D, KEY_D, KEY_A, 0}; //無後座力步槍
const byte keycmd_flamethrower[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_A, KEY_W, KEY_S, KEY_W, 0}; //火焰噴射器
const byte keycmd_autocannon[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_A, KEY_S, KEY_W, KEY_W, KEY_D, 0}; //機炮
//重機槍
const byte keycmd_railgun[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_D, KEY_A, KEY_S, KEY_W, KEY_A, KEY_D, 0}; //磁軌炮
const byte keycmd_spear[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_S, KEY_W, KEY_S, KEY_S, 0}; //長矛
//軌道大砲
const byte keycmd_380mm_barrage[MAX_COMMAND_KEY_NUM] = {KEY_D, KEY_S, KEY_W, KEY_W, KEY_A, KEY_S, KEY_S, 0}; //軌道380mm高爆彈彈幕 → ↓ ↑ ↑ ← ↓ ↓
//機庫
const byte keycmd_eagle_500kg_bomb[MAX_COMMAND_KEY_NUM] = {KEY_W, KEY_D, KEY_S, KEY_S, KEY_S, 0}; //飛鷹500kg炸彈 ↑ → ↓ ↓ ↓
//艦橋
const byte keycmd_precision_strike[MAX_COMMAND_KEY_NUM] = {KEY_D, KEY_D, KEY_W, 0}; //軌道精準攻擊
//工程灣
const byte keycmd_grenade_launcher[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_A, KEY_W, KEY_A, KEY_S, 0}; //榴彈發射器
const byte keycmd_guard_dog_rover[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_W, KEY_A, KEY_W, KEY_D, KEY_D, 0}; //護衛犬漫遊者 ↓ ↑ ← ↑ → →
//機器廠房
const byte keycmd_mortar_sentry[MAX_COMMAND_KEY_NUM] = {KEY_S, KEY_W, KEY_D, KEY_D, KEY_S, 0}; //哨戒迫擊炮 ↓ ↑ → → ↓
void check_which_key_cmd_to_send(BT_NUM current_BT)
{ //set command to button
switch (current_BT)
{
case BT_1:
send_key_cmd(keycmd_reinforce); //增援
last_BT = BT_1;
break;
case BT_2:
send_key_cmd(keycmd_resupply); //重新補給
last_BT = BT_2;
break;
case BT_3:
send_key_cmd(keycmd_stalwart); //盟友
last_BT = BT_3;
break;
case BT_4:
send_key_cmd(keycmd_flamethrower); //火焰噴射器
last_BT = BT_4;
break;
case BT_5:
send_key_cmd(keycmd_grenade_launcher); //榴彈發射器
last_BT = BT_5;
break;
case BT_6:
send_key_cmd(keycmd_precision_strike); //軌道精準攻擊
last_BT = BT_6;
break;
case BT_7:
send_key_cmd(keycmd_380mm_barrage); //軌道380mm高爆彈彈幕 → ↓ ↑ ↑ ← ↓ ↓
last_BT = BT_7;
break;
case BT_8:
send_key_cmd(keycmd_guard_dog_rover); //護衛犬漫遊者 ↓ ↑ ← ↑ → →
last_BT = BT_8;
break;
case BT_9:
last_BT = BT_9;
send_key_cmd(keycmd_mortar_sentry); //哨戒迫擊炮 ↓ ↑ → → ↓
break;
case BT_10:
last_BT = BT_10;
send_key_cmd(keycmd_eagle_500kg_bomb); //飛鷹500kg炸彈 ↑ → ↓ ↓ ↓
break;
case BT_NC:
last_BT = BT_NC;
DigiKeyboard.sendKeyStroke(0);
break;
}
}
/*****其他程式碼*****/
void check_BT_send_cmd()
{ //Check button and send key command
BT_NUM current_BT = check_BT_press();
if ((current_BT == BT_NA) || (last_BT == current_BT))
return; //No BT change, return
if ((last_BT != BT_NC) && (current_BT != BT_NC))
{ //release last button
last_BT = BT_NC;
}
check_which_key_cmd_to_send(current_BT);
}
void setup()
{
//Init var
last_read_BT = BT_NA;
last_BT = BT_NC;
last_read_BT_time = 0;
//Generate Button voltage table
Gen_BT_volt_tab();
//Setting up the internal pull-ups resistors
//and also setting the pins to inputs.
pinMode(BT_VOLT_PIN, INPUT); // analog in
pinMode(LED_R, OUTPUT);
//pinMode(pin2, INPUT);
}
void loop()
{
check_BT_send_cmd();
}
void Gen_BT_volt_tab()
{ //Generate button volt table
BT_volt[0] = GPIO_VOL * (R2 * TOTAL_BT_NUM) / (R2 * TOTAL_BT_NUM + R1); //BT_NC
BT_volt[1] = 0; //BT_1
for (int i = 2; i <= TOTAL_BT_NUM; i++)
BT_volt[i] = GPIO_VOL * (R2 * (i - 1)) / (R2 * (i - 1) + R1); //BT_2, BT_3...
}
BT_NUM check_BT_press()
{ //Check which button are press
int value = analogRead( BT_VOLT_PIN );
float volt = value * GPIO_VOL / 1023.0;
BT_NUM current_read_BT = BT_NA;
//Check which button press
for (int i = 0; i <= TOTAL_BT_NUM; i++)
{
if ((volt > BT_volt[i] - BT_volt_offset) && (volt < BT_volt[i] + BT_volt_offset))
{
current_read_BT = (BT_NUM) i;
break;
}
}
//Debounce
if ((millis() - last_read_BT_time) > deBounce_time)
{ //more than debounce time check status
last_read_BT_time = millis();
if (last_read_BT != current_read_BT)
{
last_read_BT = current_read_BT;
return BT_NA;
}
else if (last_read_BT == current_read_BT)
{
last_read_BT = current_read_BT;
return current_read_BT;
}
}
return BT_NA;
}
void send_key_cmd(const byte key_cmd[])
{
//Send key command
byte previous_key = 0;
if (key_cmd[0] == 0)
return; //an empty key cmd
//flash LED
LED_blink(LED_R, 50);
//release all keys at first
DigiKeyboard.sendKeyStroke(0, 0);
delay(100);
//Hold Left Control first
DigiKeyboard.sendKeyPress(0, MOD_CONTROL_LEFT);
delay(DELAY1);
//Start press command keys
for (int i = 0; i <= MAX_COMMAND_KEY_NUM; i++) {
if (key_cmd[i] == 0)
break; //command end
if (previous_key == key_cmd[i]) {
//release key first if the next key is the same key
DigiKeyboard.sendKeyPress(0, MOD_CONTROL_LEFT);
delay(DELAY2);
}
DigiKeyboard.sendKeyPress(key_cmd[i], MOD_CONTROL_LEFT);
delay(random(DELAY2, DELAY2 + MAX_RANDOM_DELAY));
previous_key = key_cmd[i];
}
//release all keys
DigiKeyboard.sendKeyStroke(0, 0);
delay(DELAY3);
}
void LED_blink(int LED, int time_ms)
{ //Turn on LED for certain time
digitalWrite(LED, HIGH);
delay(time_ms);
digitalWrite(LED, LOW);
}
大家有興趣的可以自己做看看.
Leave a Comment