Update lever commands

This commit is contained in:
whowechina 2024-09-17 22:28:29 +08:00
parent c622ae7ec6
commit 41c59d8319
10 changed files with 84 additions and 235 deletions

View File

@ -165,7 +165,7 @@ You need to rotate 135 degrees on Z axis to fit the bed.
4. Wire (solder) the speakers to the main PCB, each needs 2 wires.
5. Use VHB tape to fix the speakers on the floor of the bottom part.
6. Install the main PCB to the bottom part, no screws needed.
7. Bend the hall-effect sensor (SS49E) so it stays on top of the lever magnets. Remember to leave a tiny gap so the shaft can move freely.
7. Bend the hall-effect sensor (SS49E) so it stays on top of the lever magnets. Remember to leave a tiny gap so the shaft can move freely.
<img src="doc/lever_hall_1.jpg" width="40%"> <img src="doc/lever_hall_2.jpg" width="48%">
8. Solder the PN532 module, 8002A modules to the main PCB if you haven't done it yet.
9. Install the main button caps and the aux button caps onto the switches.
@ -183,7 +183,13 @@ You need to rotate 135 degrees on Z axis to fit the bed.
* If it's already running Geki Pico firmware, you can either use "update" in command line or hold down at least 4 buttons while connecting to USB to enter update mode.
* To access the command line, you can use this Web Serial Terminal to connect to the USB serial port of the Geki Pico. (Note: "?" is for help)
https://googlechromelabs.github.io/serial-terminal/
* **NOTE:** You need to calibrate the lever by "lever calibrate" command after you flash the firmware.
### Usage
* You need to calibrate the lever by "lever calibrate" command after you flash the firmware.
* If you feel the lever direction is in correct, you can use "lever invert \<on|off\>" command to change it.
* Volume of sound feedback can be set by "volume \<0~255\>" command.
* To emulate IO4 TEST/SERVICE/COIN, you can put your hand at higher position over the right ToF sensor. When you see the WAD lights flashing, the AUX buttons become TEST and SERVICE, swing the lever for "INSERT COINS".
* AIME is on a secondary COM port. You can set mode or toggle virtual AIC function.
## CAD Source File
I'm using OnShape free subscription. It's powerful but it can't archive original designs to local, so I can only share the link here. STL/DXF/DWG files are exported from this online document.

View File

@ -1,7 +1,7 @@
function(make_firmware board board_def)
pico_sdk_init()
add_executable(${board}
main.c light.c button.c gimbal.c sound.c airkey.c vl53l0x.c save.c config.c
main.c light.c button.c lever.c sound.c airkey.c vl53l0x.c save.c config.c
commands.c cli.c hid.c usb_descriptors.c)
target_compile_definitions(${board} PUBLIC ${board_def})
pico_enable_stdio_usb(${board} 1)

View File

@ -11,7 +11,7 @@
#include "save.h"
#include "cli.h"
#include "gimbal.h"
#include "lever.h"
extern uint8_t RING_DATA[];
@ -23,52 +23,18 @@ extern uint8_t RING_DATA[];
#define SENSE_LIMIT_MAX 9
#define SENSE_LIMIT_MIN -9
static inline int sprintf_hsv_rgb(char *buf, const rgb_hsv_t *color)
{
return sprintf(buf, "%s(%d,%d,%d)", color->rgb_hsv ? "hsv" : "rgb",
color->val[0], color->val[1], color->val[2]);
}
static const char *color_str(const rgb_hsv_t *color, bool left_right)
{
static char buf[64];
int count = 0;
if (left_right) {
count += sprintf(buf + count, "LEFT ");
}
count += sprintf_hsv_rgb(buf + count, color);
if (left_right) {
count += sprintf(buf + count, ", RIGHT ");
count += sprintf_hsv_rgb(buf + count, color + 1);
}
return buf;
}
static void disp_light()
{
printf("[Light]\n");
printf(" Level: %d.\n", geki_cfg->light.level);
printf(" Colors:\n");
printf(" base0: %s\n", color_str(geki_cfg->light.base[0], true));
printf(" base1: %s\n", color_str(geki_cfg->light.base[0], true));
printf(" button: %s\n", color_str(geki_cfg->light.button, true));
printf(" boost: %s\n", color_str(geki_cfg->light.boost, true));
printf(" steer: %s\n", color_str(geki_cfg->light.steer, true));
printf(" aux_on: %s\n", color_str(&geki_cfg->light.aux_on, false));
printf(" aux_off: %s\n", color_str(&geki_cfg->light.aux_off, false));
}
static void disp_gimbal()
static void disp_lever()
{
printf("[Gimbal]\n");
printf(" %s, %s, raw %d-%d.\n",
geki_cfg->gimbal.invert ? "invert" : "normal",
geki_cfg->gimbal.analog ? "analog" : "digital",
geki_cfg->gimbal.min, geki_cfg->gimbal.max);
printf("[Lever]\n");
printf(" %s, raw %d-%d.\n",
geki_cfg->lever.invert ? "invert" : "normal",
geki_cfg->lever.min, geki_cfg->lever.max);
}
static void disp_sound()
@ -95,7 +61,7 @@ static void disp_aime()
void handle_display(int argc, char *argv[])
{
const char *usage = "Usage: display [light|sound|hid|gimbal|aime]\n";
const char *usage = "Usage: display [light|sound|hid|lever|aime]\n";
if (argc > 1) {
printf(usage);
return;
@ -103,20 +69,20 @@ void handle_display(int argc, char *argv[])
if (argc == 0) {
disp_light();
disp_gimbal();
disp_lever();
disp_sound();
disp_hid();
disp_aime();
return;
}
const char *choices[] = {"light", "gimbal", "sound", "hid", "aime"};
const char *choices[] = {"light", "lever", "sound", "hid", "aime"};
switch (cli_match_prefix(choices, count_of(choices), argv[0])) {
case 0:
disp_light();
break;
case 1:
disp_gimbal();
disp_lever();
break;
case 2:
disp_sound();
@ -198,7 +164,7 @@ static void calibrate_range(uint32_t seconds)
uint64_t start = time_us_64();
while (time_us_64() - start < seconds * 1000000) {
uint16_t val = gimbal_raw();
uint16_t val = lever_raw();
printf("%4d\n", val);
if (val < mins) {
mins -= (mins - val) / 2;
@ -208,13 +174,13 @@ static void calibrate_range(uint32_t seconds)
sleep_ms(7);
}
geki_cfg->gimbal.min = mins;
geki_cfg->gimbal.max = maxs;
geki_cfg->lever.min = mins;
geki_cfg->lever.max = maxs;
}
static void gimbal_calibrate()
static void lever_calibrate()
{
printf("Slowly move the stick in full range.\n");
printf("Slowly swing the lever in full range.\n");
printf("Now calibrating ...");
fflush(stdout);
@ -222,9 +188,9 @@ static void gimbal_calibrate()
printf(" done.\n");
}
static void gimbal_invert(const char *param)
static void lever_invert(const char *param)
{
const char *usage = "Usage: gimbal invert <on|off>\n";
const char *usage = "Usage: lever invert <on|off>\n";
int invert = cli_match_prefix((const char *[]){"off", "on"}, 2, param);
if (invert < 0) {
@ -234,125 +200,33 @@ static void gimbal_invert(const char *param)
printf("param:%s, invert:%d\n", param, invert);
geki_cfg->gimbal.invert = invert;
geki_cfg->lever.invert = invert;
}
static void gimbal_analog(const char *param)
static void handle_lever(int argc, char *argv[])
{
const char *usage = "Usage: gimbal analog <on|off>\n";
int analog = cli_match_prefix((const char *[]){"off", "on"}, 2, param);
if (analog < 0) {
printf(usage);
return;
}
const char *usage = "Usage: lever calibrate\n"
" lever invert <on|off>\n";
geki_cfg->gimbal.analog = analog;
}
static void handle_gimbal(int argc, char *argv[])
{
const char *usage = "Usage: gimbal calibrate\n"
" gimbal invert <on|off>\n"
" gimbal analog <on|off>\n";
if (argc == 1) {
if (strncasecmp(argv[0], "calibrate", strlen(argv[0])) != 0) {
printf(usage);
return;
}
gimbal_calibrate();
lever_calibrate();
} else if (argc == 2) {
int op = cli_match_prefix((const char *[]){"invert", "analog"}, 2, argv[0]);
if (op == 0) {
gimbal_invert(argv[1]);
} else if (op == 1) {
gimbal_analog(argv[1]);
} else {
if (strncasecmp(argv[0], "invert", strlen(argv[0])) != 0) {
printf(usage);
return;
}
lever_invert(argv[1]);
} else {
printf(usage);
return;
}
config_changed();
disp_gimbal();
}
static bool extract_color(rgb_hsv_t *color, char *argv[4])
{
int rgb_hsv = cli_match_prefix((const char *[]){"rgb", "hsv"}, 2, argv[0]);
if (rgb_hsv < 0) {
return false;
}
color->rgb_hsv = rgb_hsv;
for (int i = 0; i < 3; i++) {
int v = cli_extract_non_neg_int(argv[1 + i], 0);
if ((v < 0) || (v > 255)) {
return false;
}
color->val[i] = v;
}
return true;
}
static void handle_color(int argc, char *argv[])
{
const char *usage = "Usage: color <name> [left|right] <rgb|hsv> <0..255> <0..255> <0..255>\n"
" name: base0 base1 button boost steer aux_on aux_off\n";
if ((argc != 5) && (argc != 6)) {
printf(usage);
return;
}
rgb_hsv_t *names[] = {
&geki_cfg->light.aux_on,
&geki_cfg->light.aux_off,
geki_cfg->light.base[0],
geki_cfg->light.base[1],
geki_cfg->light.button,
geki_cfg->light.boost,
geki_cfg->light.steer,
};
const char *choices[] = {"aux_on", "aux_off", "base0", "base1", "button", "boost", "steer"};
static_assert(count_of(choices) == count_of(names));
int name = cli_match_prefix(choices, count_of(choices), argv[0]);
if (name < 0) {
printf(usage);
return;
}
bool left = true;
bool right = true;
if (argc == 6) {
int left_right = cli_match_prefix((const char *[]){"left", "right"}, 2, argv[1]);
if (left_right < 0) {
printf(usage);
return;
}
left = (left_right == 0);
right = (left_right == 1);
}
rgb_hsv_t color;
if (!extract_color(&color, argv + argc - 4)) {
printf(usage);
return;
}
rgb_hsv_t *target = names[name];
if (left) {
target[0] = color;
}
if ((name >= 2) && right) {
target[1] = color;
}
config_changed();
disp_light();
disp_lever();
}
static void handle_save()
@ -458,9 +332,8 @@ void commands_init()
{
cli_register("display", handle_display, "Display all config.");
cli_register("level", handle_level, "Set LED brightness level.");
cli_register("color", handle_color, "Set LED color.");
cli_register("hid", handle_hid, "Set HID mode.");
cli_register("gimbal", handle_gimbal, "Calibrate the gimbals.");
cli_register("lever", handle_lever, "Lever related settings.");
cli_register("volume", handle_volume, "Sound feedback volume settings.");
cli_register("save", handle_save, "Save config to flash.");
cli_register("factory", handle_factory_reset, "Reset everything to default.");

View File

@ -12,35 +12,11 @@
geki_cfg_t *geki_cfg;
static geki_cfg_t default_cfg = {
.gimbal = {
2000, 2500, 0, 80, 1,
.lever = {
2000, 2500, 0,
},
.light = {
.level = 128,
.base = {
{
{ 1, { 20, 150, 10 }, },
{ 1, { 147, 150, 10 }, },
},
{
{ 1, { 20, 150, 30 }, },
{ 1, { 147, 150, 30 }, },
},
},
.button = {
{ 1, { 0, 0, 120 } },
{ 1, { 0, 0, 120 } },
},
.boost = {
{ 1, { 20, 255, 255 } },
{ 1, { 147, 255, 255 } },
},
.steer = {
{ 1, { 80, 255, 255 } },
{ 1, { 80, 255, 255 } },
},
.aux_on = { 0, { 100, 100, 100 } },
.aux_off = { 0, { 8, 8, 8 } },
.reserved = { 0 },
},
.sound = {

View File

@ -19,16 +19,10 @@ typedef struct __attribute__((packed)) {
uint16_t min;
uint16_t max;
uint8_t invert:1;
uint8_t threshold:7;
uint8_t analog:1;
} gimbal;
uint8_t reserved:7;
} lever;
struct {
rgb_hsv_t base[2][2];
rgb_hsv_t button[2];
rgb_hsv_t boost[2];
rgb_hsv_t steer[2];
rgb_hsv_t aux_on;
rgb_hsv_t aux_off;
rgb_hsv_t colors[12];
uint8_t level;
uint8_t reserved[15];
} light;

View File

@ -1,18 +0,0 @@
/*
* Left and Right Gimbal Inputs
* WHowe <github.com/whowechina>
*/
#ifndef GIMBAL_H
#define GIMBAL_H
#include <stdint.h>
#include <stdbool.h>
void gimbal_init();
uint8_t gimbal_read();
uint16_t gimbal_raw();
uint16_t gimbal_average();
#endif

View File

@ -6,7 +6,7 @@
#include "tusb.h"
#include "usb_descriptors.h"
#include "button.h"
#include "gimbal.h"
#include "lever.h"
#include "airkey.h"
#include "config.h"
#include "light.h"
@ -30,7 +30,7 @@ struct __attribute__((packed)) {
static void gen_hid_analogs()
{
hid_joy.adcs[0] = gimbal_read() << 8;
hid_joy.adcs[0] = lever_read() << 8;
}
static void gen_hid_buttons()
{
@ -80,14 +80,14 @@ static void gen_hid_buttons()
static void gen_hid_coins()
{
static uint8_t last_gimbal = 0;
uint8_t gimbal = gimbal_read();
static uint8_t last_lever = 0;
uint8_t lever = lever_read();
static int dec_count = 0;
if (airkey_get(3)) {
if (gimbal < last_gimbal) {
if (lever < last_lever) {
dec_count++;
} else if (gimbal > last_gimbal) {
} else if (lever > last_lever) {
dec_count = 0;
}
@ -97,7 +97,7 @@ static void gen_hid_coins()
}
}
last_gimbal = gimbal;
last_lever = lever;
}
static void report_usb_hid()
{

View File

@ -1,10 +1,10 @@
/*
* Left and Right Gimbal Inputs
* Lever Input
* WHowe <github.com/whowechina>
*
*/
#include "gimbal.h"
#include "lever.h"
#include <stdint.h>
#include <stdio.h>
@ -20,7 +20,7 @@
#include "config.h"
#include "board_defs.h"
void gimbal_init()
void lever_init()
{
gpio_init(AXIS_MUX_PIN_A);
gpio_set_dir(AXIS_MUX_PIN_A, GPIO_OUT);
@ -33,11 +33,11 @@ void gimbal_init()
adc_select_input(ADC_CHANNEL);
}
uint8_t gimbal_read()
uint8_t lever_read()
{
uint16_t val = gimbal_average();
const uint16_t min = geki_cfg->gimbal.min;
const uint16_t max = geki_cfg->gimbal.max;
uint16_t val = lever_average();
const uint16_t min = geki_cfg->lever.min;
const uint16_t max = geki_cfg->lever.max;
if (val < min) {
val = min;
@ -51,14 +51,14 @@ uint8_t gimbal_read()
}
uint8_t result = (val - min) * 255 / range;
if (geki_cfg->gimbal.invert) {
if (geki_cfg->lever.invert) {
result = 255 - result;
}
return result;
}
uint16_t gimbal_raw()
uint16_t lever_raw()
{
static uint16_t last_read = 2048;
const uint16_t rate_limit = 5;
@ -75,18 +75,18 @@ uint16_t gimbal_raw()
return last_read;
}
#define GIMBAL_AVERAGE_COUNT 32
uint16_t gimbal_average()
#define LEVER_AVERAGE_COUNT 32
uint16_t lever_average()
{
static uint16_t buf[GIMBAL_AVERAGE_COUNT] = {0};
static uint16_t buf[LEVER_AVERAGE_COUNT] = {0};
static int index = 0;
index = (index + 1) % GIMBAL_AVERAGE_COUNT;
buf[index] = gimbal_raw();
index = (index + 1) % LEVER_AVERAGE_COUNT;
buf[index] = lever_raw();
uint32_t sum = 0;
for (int i = 0; i < GIMBAL_AVERAGE_COUNT; i++) {
for (int i = 0; i < LEVER_AVERAGE_COUNT; i++) {
sum += buf[i];
}
return sum / GIMBAL_AVERAGE_COUNT;
return sum / LEVER_AVERAGE_COUNT;
}

18
firmware/src/lever.h Normal file
View File

@ -0,0 +1,18 @@
/*
* Lever Input
* WHowe <github.com/whowechina>
*/
#ifndef LEVER_H
#define LEVER_H
#include <stdint.h>
#include <stdbool.h>
void lever_init();
uint8_t lever_read();
uint16_t lever_raw();
uint16_t lever_average();
#endif

View File

@ -35,13 +35,13 @@
#include "light.h"
#include "button.h"
#include "gimbal.h"
#include "lever.h"
#include "airkey.h"
#include "sound.h"
static void run_lights()
{
light_set_pos(255 - gimbal_read(), rgb32(0xff, 0, 0, false));
light_set_pos(255 - lever_read(), rgb32(0xff, 0, 0, false));
uint16_t button = button_read();
@ -174,7 +174,7 @@ void init()
light_init();
button_init();
gimbal_init();
lever_init();
airkey_init();
sound_init();