diff --git a/README.md b/README.md index 92047d6..76f9867 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,10 @@ This was ported from the [TWANG fork](https://github.com/bdring/TWANG) by bdring ![](http://www.buildlog.net/blog/wp-content/uploads/2018/03/20180328_122254.jpg) -Coming Soon:** +## Coming Soon: +- Setting + - I want to figure out a way to have the LED count be set via the web server. I often bring several length LED strings to an event because I don't know which size is most appropriate. A last minute recompile is not a good solution. - Wireless features~~ - 2 Player features by linking controllers. TBD - Digitized Audio diff --git a/TWANG32.ino b/TWANG32.ino index 9ec721f..e638d09 100644 --- a/TWANG32.ino +++ b/TWANG32.ino @@ -7,10 +7,19 @@ https://github.com/Critters/TWANG It was inspired by Robin Baumgarten's Line Wobbler Game + + Recent Changes + - fixed bug in settings when initial read failed. It was + trying to turn off the sound timer while it was still NULL + - Number of LEDs is now adjustable via serial and wifi. Woot, no + recompile just to change strip length. + - A little refactoring so Wifi and Serial interfaces can share + some code. + - Added Refresh button to web page */ -#define VERSION "2018-03-21" +#define VERSION "2018-03-31" #include #include @@ -29,15 +38,12 @@ #include "sound.h" #include "settings.h" #include "wifi_ap.h" -//#include "SoundData.h"; -//#include "Game_Audio.h" #define DATA_PIN 16 #define CLOCK_PIN 17 -#define LED_TYPE APA102 -//#define LED_COLOR_ORDER BGR -#define NUM_LEDS 144 + +#define VIRTUAL_LED_COUNT 1000 // what type of LED Strip....pick one #define USE_APA102 @@ -66,8 +72,8 @@ // GAME long previousMillis = 0; // Time of the last redraw int levelNumber = 0; -long lastInputTime = 0; -#define TIMEOUT 30000 // time until screen saver + +#define TIMEOUT 20000 // time until screen saver in milliseconds @@ -96,12 +102,12 @@ bool attacking = 0; // Is the attack in progress? #define WIN_OFF_DURATION 1200 Twang_MPU accelgyro = Twang_MPU(); -CRGB leds[NUM_LEDS]; +CRGB leds[VIRTUAL_LED_COUNT]; RunningMedian MPUAngleSamples = RunningMedian(5); RunningMedian MPUWobbleSamples = RunningMedian(5); iSin isin = iSin(); -//#define DEBUG // comment out to stop serial debugging +#define JOYSTICK_DEBUG // comment out to stop serial debugging // POOLS #define LIFE_LEDS 3 @@ -165,7 +171,7 @@ void setup() { Wire.begin(); accelgyro.initialize(); - FastLED.addLeds(leds, NUM_LEDS); + FastLED.addLeds(leds, VIRTUAL_LED_COUNT); FastLED.setBrightness(user_settings.led_brightness); FastLED.setDither(1); @@ -252,10 +258,10 @@ void loop() { playerPosition = 0; // stop player from leaving if boss is alive - if (boss.Alive() && playerPosition >= 1000) // move player back - playerPosition = 999; //(NUM_LEDS - 1) * (1000.0/NUM_LEDS); + if (boss.Alive() && playerPosition >= VIRTUAL_LED_COUNT) // move player back + playerPosition = 999; //(user_settings.led_count - 1) * (1000.0/user_settings.led_count); - if(playerPosition >= 1000 && !boss.Alive()) { + if(playerPosition >= VIRTUAL_LED_COUNT && !boss.Alive()) { // Reached exit! levelComplete(); return; @@ -393,13 +399,13 @@ void loadLevel(){ break; case 2: // Spawning enemies at exit every 2 seconds - spawnPool[0].Spawn(1000, 3000, 2, 0, 0); + spawnPool[0].Spawn(VIRTUAL_LED_COUNT, 3000, 2, 0, 0); break; case 3: // Lava intro spawnLava(400, 490, 2000, 2000, 0, Lava::OFF); spawnEnemy(350, 0, 1, 0); - spawnPool[0].Spawn(1000, 5500, 3, 0, 0); + spawnPool[0].Spawn(VIRTUAL_LED_COUNT, 5500, 3, 0, 0); break; case 4: @@ -426,7 +432,7 @@ void loadLevel(){ break; case 7: // Conveyor of enemies - spawnConveyor(50, 1000, 6); + spawnConveyor(50, VIRTUAL_LED_COUNT, 6); spawnEnemy(300, 0, 0, 0); spawnEnemy(400, 0, 0, 0); spawnEnemy(500, 0, 0, 0); @@ -436,7 +442,7 @@ void loadLevel(){ spawnEnemy(900, 0, 0, 0); break; case 8: // spawn train; - spawnPool[0].Spawn(900, 1000, 3, 0, 0); + spawnPool[0].Spawn(900, VIRTUAL_LED_COUNT, 3, 0, 0); break; case 9: // spawn train skinny width; attack_width = 32; @@ -456,13 +462,13 @@ void loadLevel(){ spawnLava(195, 300, 2000, 2000, 0, Lava::OFF); spawnLava(400, 500, 2000, 2000, 0, Lava::OFF); spawnLava(600, 700, 2000, 2000, 0, Lava::OFF); - spawnPool[0].Spawn(1000, 3800, 4, 0, 0); + spawnPool[0].Spawn(VIRTUAL_LED_COUNT, 3800, 4, 0, 0); break; case 13: // Sin enemy #2 practice (slow conveyor) spawnEnemy(700, 1, 7, 275); spawnEnemy(500, 1, 5, 250); - spawnPool[0].Spawn(1000, 5500, 4, 0, 3000); + spawnPool[0].Spawn(VIRTUAL_LED_COUNT, 5500, 4, 0, 3000); spawnPool[1].Spawn(0, 5500, 5, 1, 10000); spawnConveyor(100, 900, -4); break; @@ -470,7 +476,7 @@ void loadLevel(){ // Sin enemy #2 (fast conveyor) spawnEnemy(700, 1, 7, 275); spawnEnemy(500, 1, 5, 250); - spawnPool[0].Spawn(1000, 5500, 4, 0, 3000); + spawnPool[0].Spawn(VIRTUAL_LED_COUNT, 5500, 4, 0, 3000); spawnPool[1].Spawn(0, 5500, 5, 1, 10000); spawnConveyor(100, 900, -6); break; @@ -609,14 +615,14 @@ void tickStartup(long mm) FastLED.clear(); if(stageStartTime+STARTUP_WIPEUP_DUR > mm) // fill to the top with green { - int n = _min(map(((mm-stageStartTime)), 0, STARTUP_WIPEUP_DUR, 0, NUM_LEDS), NUM_LEDS); // fill from top to bottom + int n = _min(map(((mm-stageStartTime)), 0, STARTUP_WIPEUP_DUR, 0, user_settings.led_count), user_settings.led_count); // fill from top to bottom for(int i = 0; i<= n; i++){ leds[i] = CRGB(0, 255, 0); } } else if(stageStartTime+STARTUP_SPARKLE_DUR > mm) // sparkle the full green bar { - for(int i = 0; i< NUM_LEDS; i++){ + for(int i = 0; i< user_settings.led_count; i++){ if(random8(30) < 28) leds[i] = CRGB(0, 255, 0); // most are green else { @@ -628,13 +634,13 @@ void tickStartup(long mm) } else if (stageStartTime+STARTUP_FADE_DUR > mm) // fade it out to bottom { - int n = _max(map(((mm-stageStartTime)), STARTUP_SPARKLE_DUR, STARTUP_FADE_DUR, 0, NUM_LEDS), 0); // fill from top to bottom + int n = _max(map(((mm-stageStartTime)), STARTUP_SPARKLE_DUR, STARTUP_FADE_DUR, 0, user_settings.led_count), 0); // fill from top to bottom int brightness = _max(map(((mm-stageStartTime)), STARTUP_SPARKLE_DUR, STARTUP_FADE_DUR, 255, 0), 0); // for(int i = 0; i<= n; i++){ // leds[i] = CRGB(0, brightness, 0); // } - for(int i = n; i< NUM_LEDS; i++){ + for(int i = n; i< user_settings.led_count; i++){ leds[i] = CRGB(0, brightness, 0); } @@ -713,7 +719,7 @@ void drawPlayer(){ void drawExit(){ if(!boss.Alive()){ - leds[NUM_LEDS-1] = CRGB(0, 0, 255); + leds[user_settings.led_count-1] = CRGB(0, 0, 255); } } @@ -827,18 +833,18 @@ void tickComplete(long mm) // the boss is dead FastLED.clear(); SFXcomplete(); if(stageStartTime+500 > mm){ - int n = _max(map(((mm-stageStartTime)), 0, 500, NUM_LEDS, 0), 0); - for(int i = NUM_LEDS; i>= n; i--){ + int n = _max(map(((mm-stageStartTime)), 0, 500, user_settings.led_count, 0), 0); + for(int i = user_settings.led_count; i>= n; i--){ brightness = (sin(((i*10)+mm)/500.0)+1)*255; leds[i].setHSV(brightness, 255, 50); } }else if(stageStartTime+5000 > mm){ - for(int i = NUM_LEDS; i>= 0; i--){ + for(int i = user_settings.led_count; i>= 0; i--){ brightness = (sin(((i*10)+mm)/500.0)+1)*255; leds[i].setHSV(brightness, 255, 50); } }else if(stageStartTime+5500 > mm){ - int n = _max(map(((mm-stageStartTime)), 5000, 5500, NUM_LEDS, 0), 0); + int n = _max(map(((mm-stageStartTime)), 5000, 5500, user_settings.led_count, 0), 0); for(int i = 0; i< n; i++){ brightness = (sin(((i*10)+mm)/500.0)+1)*255; leds[i].setHSV(brightness, 255, 50); @@ -853,20 +859,20 @@ void tickBossKilled(long mm) // boss funeral int brightness = 0; FastLED.clear(); if(stageStartTime+500 > mm){ - int n = _max(map(((mm-stageStartTime)), 0, 500, NUM_LEDS, 0), 0); - for(int i = NUM_LEDS; i>= n; i--){ + int n = _max(map(((mm-stageStartTime)), 0, 500, user_settings.led_count, 0), 0); + for(int i = user_settings.led_count; i>= n; i--){ brightness = (sin(((i*10)+mm)/500.0)+1)*255; leds[i].setHSV(brightness, 255, 50); } SFXbosskilled(); }else if(stageStartTime+6500 > mm){ - for(int i = NUM_LEDS; i>= 0; i--){ + for(int i = user_settings.led_count; i>= 0; i--){ brightness = (sin(((i*10)+mm)/500.0)+1)*255; leds[i].setHSV(brightness, 255, 50); } SFXbosskilled(); }else if(stageStartTime+7000 > mm){ - int n = _max(map(((mm-stageStartTime)), 5000, 5500, NUM_LEDS, 0), 0); + int n = _max(map(((mm-stageStartTime)), 5000, 5500, user_settings.led_count, 0), 0); for(int i = 0; i< n; i++){ brightness = (sin(((i*10)+mm)/500.0)+1)*255; leds[i].setHSV(brightness, 255, 50); @@ -907,7 +913,7 @@ void tickGameover(long mm) { if(stageStartTime+GAMEOVER_SPREAD_DURATION > mm) // Spread red from player position to top and bottom { // fill to top - int n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(playerPosition), NUM_LEDS), 0); + int n = _max(map(((mm-stageStartTime)), 0, GAMEOVER_SPREAD_DURATION, getLED(playerPosition), user_settings.led_count), 0); for(int i = getLED(playerPosition); i<= n; i++){ leds[i] = CRGB(255, 0, 0); } @@ -920,7 +926,7 @@ void tickGameover(long mm) { } else if(stageStartTime+GAMEOVER_FADE_DURATION > mm) // fade down to bottom and fade brightness { - int n = _max(map(((mm-stageStartTime)), GAMEOVER_FADE_DURATION, GAMEOVER_SPREAD_DURATION, NUM_LEDS, 0), 0); + int n = _max(map(((mm-stageStartTime)), GAMEOVER_FADE_DURATION, GAMEOVER_SPREAD_DURATION, user_settings.led_count, 0), 0); brightness = map(((mm-stageStartTime)), GAMEOVER_SPREAD_DURATION, GAMEOVER_FADE_DURATION, 200, 0); for(int i = 0; i<= n; i++){ @@ -937,14 +943,14 @@ void tickWin(long mm) { int brightness = 0; FastLED.clear(); if(stageStartTime+WIN_FILL_DURATION > mm){ - int n = _max(map(((mm-stageStartTime)), 0, WIN_FILL_DURATION, NUM_LEDS, 0), 0); // fill from top to bottom - for(int i = NUM_LEDS; i>= n; i--){ + int n = _max(map(((mm-stageStartTime)), 0, WIN_FILL_DURATION, user_settings.led_count, 0), 0); // fill from top to bottom + for(int i = user_settings.led_count; i>= n; i--){ brightness = user_settings.led_brightness; leds[i] = CRGB(0, brightness, 0); } SFXwin(); }else if(stageStartTime+WIN_CLEAR_DURATION > mm){ - int n = _max(map(((mm-stageStartTime)), WIN_FILL_DURATION, WIN_CLEAR_DURATION, NUM_LEDS, 0), 0); // clear from top to bottom + int n = _max(map(((mm-stageStartTime)), WIN_FILL_DURATION, WIN_CLEAR_DURATION, user_settings.led_count, 0), 0); // clear from top to bottom for(int i = 0; i< n; i++){ brightness = user_settings.led_brightness; leds[i] = CRGB(0, brightness, 0); @@ -1001,7 +1007,7 @@ void drawAttack(){ int getLED(int pos){ // The world is 1000 pixels wide, this converts world units into an LED number - return constrain((int)map(pos, 0, 1000, 0, NUM_LEDS-1), 0, NUM_LEDS-1); + return constrain((int)map(pos, 0, VIRTUAL_LED_COUNT, 0, user_settings.led_count-1), 0, user_settings.led_count-1); } bool inLava(int pos){ @@ -1042,9 +1048,9 @@ void screenSaverTick(){ long mm = millis(); int mode = (mm/30000)%5; - SFXcomplete(); // make sure there is not sound...play testing showed this to be a problem + SFXcomplete(); // turn off sound...play testing showed this to be a problem - for(i = 0; i= 1){ // Random flashes randomSeed(mm); - for(i = 0; i 0) a -= user_settings.joystick_deadzone; @@ -1097,6 +1111,12 @@ void getInput(){ joystickTilt = 0-joystickTilt; } joystickWobble = abs(MPUWobbleSamples.getHighest()); + + #ifdef JOYSTICK_DEBUG + //Serial.print("tilt:"); Serial.println(joystickTilt); + //Serial.print("wobble:"); Serial.println(joystickWobble); + #endif + } // --------------------------------- diff --git a/settings.h b/settings.h index d4336da..a3eedf1 100644 --- a/settings.h +++ b/settings.h @@ -4,12 +4,19 @@ #include #include "sound.h" +// Version 2 adds the number of LEDs + // change this whenever the saved settings are not compatible with a change // It forces a reset from defaults. -#define SETTINGS_VERSION 1 +#define SETTINGS_VERSION 2 #define EEPROM_SIZE 256 // LEDS +#define NUM_LEDS 144 +#define MIN_LEDS 60 +#define MAX_LEDS 1000 + + #define DEFAULT_BRIGHTNESS 150 #define MIN_BRIGHTNESS 10 #define MAX_BRIGHTNESS 255 @@ -47,7 +54,8 @@ enum ErrorNums{ ERR_SETTING_RANGE }; -//EEPROMClass SETTINGS("eeprom", 0x100); +long lastInputTime = 0; + //TODO ... move all the settings to this file. @@ -57,11 +65,12 @@ void settings_init(); void show_game_stats(); void settings_eeprom_write(); void settings_eeprom_read(); -void change_setting(char *line); +void change_setting_serial(char *line); void processSerial(char inChar); void printError(int reason); void show_settings_menu(); void reset_settings(); +void change_setting(char paramCode, uint16_t newValue); SemaphoreHandle_t xMutex; @@ -69,7 +78,7 @@ SemaphoreHandle_t xMutex; typedef struct { uint8_t settings_version; // stores the settings format version - //due to the fastLED classes there is not much we can change dynamically + uint16_t led_count; uint8_t led_brightness; uint8_t joystick_deadzone; @@ -121,8 +130,6 @@ void processSerial(char inChar) readIndex = 0; reset_settings(); settings_eeprom_write(); - delay(1000); - ESP.restart(); return; break; @@ -135,6 +142,8 @@ void processSerial(char inChar) return; break; + case '!': + ESP.restart(); default: break; @@ -147,7 +156,7 @@ void processSerial(char inChar) } else { readBuffer[readIndex] = 0; // mark it as the end of the string - change_setting(readBuffer); + change_setting_serial(readBuffer); readIndex = 0; } } @@ -158,7 +167,7 @@ void processSerial(char inChar) readIndex++; } -void change_setting(char *line) { +void change_setting_serial(char *line) { // line formate should be ss=nn // ss is always a 2 character integer // nn starts at index 3 and can be up to a 5 character unsigned integer @@ -185,6 +194,7 @@ void change_setting(char *line) { else { Serial.println("Invalid setting value"); return; + } } else @@ -195,72 +205,67 @@ void change_setting(char *line) { newValue = atoi(setting_val); // convert the val section to an integer memset(readBuffer,0,sizeof(readBuffer)); + + change_setting(param, newValue); + - switch (param) { - case 'B': // brightness - if(newValue >= MIN_BRIGHTNESS && newValue <= MAX_BRIGHTNESS) { - user_settings.led_brightness = (uint8_t)newValue; +} + +void change_setting(char paramCode, uint16_t newValue) +{ + switch (paramCode) { + + + lastInputTime = millis(); // reset screensaver count + + case 'C': // LED Count + user_settings.led_count = constrain(newValue, MIN_LEDS, MAX_LEDS); + settings_eeprom_write(); + break; + + case 'B': // brightness + user_settings.led_brightness = constrain(newValue, MIN_BRIGHTNESS, MAX_BRIGHTNESS); + settings_eeprom_write(); + break; + + case 'S': // sound + user_settings.audio_volume = constrain(newValue, MIN_VOLUME, MAX_VOLUME); settings_eeprom_write(); - FastLED.setBrightness(user_settings.led_brightness); - //delay(1000); - //ESP.restart(); // this one requires a restart right now - } - else { - printError(ERR_SETTING_RANGE); + break; + + case 'D': // deadzone, joystick + user_settings.joystick_deadzone = constrain(newValue, MIN_JOYSTICK_DEADZONE, MAX_JOYSTICK_DEADZONE); + settings_eeprom_write(); + break; + + case 'A': // attack threshold, joystick + user_settings.attack_threshold = constrain(newValue, MIN_ATTACK_THRESHOLD, MAX_ATTACK_THRESHOLD); + settings_eeprom_write(); + break; + + case 'L': // lives per level + user_settings.lives_per_level = constrain(newValue, MIN_LIVES_PER_LEVEL, MAX_LIVES_PER_LEVEL); + settings_eeprom_write(); + break; + + default: + Serial.print("Command Error: "); + Serial.println(readBuffer[0]); return; - } - break; - - case 'S': // sound - if (newValue >=MIN_VOLUME && newValue <= MAX_VOLUME) - user_settings.audio_volume = (uint8_t)newValue; - else { - printError(ERR_SETTING_RANGE); - return; - } - break; - - case 'D': // deadzone, joystick - if(newValue >=MIN_JOYSTICK_DEADZONE && newValue <=MAX_JOYSTICK_DEADZONE) - user_settings.joystick_deadzone = (uint8_t)newValue; - else { - printError(ERR_SETTING_RANGE); - return; - } - break; - - case 'A': // attack threshold, joystick - if(newValue >=MIN_ATTACK_THRESHOLD && newValue <=MAX_ATTACK_THRESHOLD) - user_settings.attack_threshold = (uint16_t)newValue; - else { - printError(ERR_SETTING_RANGE); - return; - } - break; - - case 'L': // lives per level - if (newValue >= 3 && newValue <= 9) - user_settings.lives_per_level = (uint8_t)newValue; - else { - printError(ERR_SETTING_RANGE); - return; - } - break; - - default: - Serial.print("Command Error: "); - Serial.println(readBuffer[0]); - return; - break; + break; } - - show_settings_menu(); + + show_settings_menu(); + } void reset_settings() { + + user_settings.settings_version = SETTINGS_VERSION; + user_settings.led_count = NUM_LEDS; user_settings.led_brightness = DEFAULT_BRIGHTNESS; user_settings.joystick_deadzone = DEFAULT_JOYSTICK_DEADZONE; @@ -275,6 +280,8 @@ void reset_settings() { user_settings.high_score = 0; user_settings.boss_kills = 0; + Serial.println("Settings reset..."); + settings_eeprom_write(); } @@ -285,9 +292,13 @@ void show_settings_menu() { Serial.println("= with a carriage return ="); Serial.println("==================================="); - Serial.print("\r\nB="); + Serial.print("\r\nC="); + Serial.print(user_settings.led_count); + Serial.println(" (LED Count 100-1000..forces restart)"); + + Serial.print("B="); Serial.print(user_settings.led_brightness); - Serial.println(" (LED Brightness 5-255..forces restart)"); + Serial.println(" (LED Brightness 5-255)"); Serial.print("S="); Serial.print(user_settings.audio_volume); @@ -332,6 +343,7 @@ void settings_eeprom_read() { if (ver != SETTINGS_VERSION) { Serial.print("Error: EEPROM settings read failed:"); Serial.println(ver); Serial.println("Loading defaults..."); + EEPROM.end(); reset_settings(); return; } @@ -354,10 +366,16 @@ void settings_eeprom_read() { void settings_eeprom_write() { + Serial.println("Settings write..."); + sound_pause(); // prevent interrupt from causing crash + + EEPROM.begin(EEPROM_SIZE); + Serial.println("EEPROM open for write..."); + uint8_t temp[sizeof(user_settings)]; memcpy(temp, (uint8_t*)&user_settings, sizeof(user_settings)); diff --git a/sound.h b/sound.h index d0dbfdb..84c5155 100644 --- a/sound.h +++ b/sound.h @@ -58,12 +58,14 @@ void sound_init(int pin){ // pin must be a DAC pin number !! (typically 25 or 2 void sound_pause() // this prevents the interrupt from firing ... use during eeprom write { - timerStop(sndTimer); + if (sndTimer != NULL) + timerStop(sndTimer); } void sound_resume() // resume from pause ... after eeprom write { - timerRestart(sndTimer); + if (sndTimer != NULL) + timerRestart(sndTimer); } bool sound(uint16_t freq, uint8_t volume){ diff --git a/wifi_ap.h b/wifi_ap.h index 5373d2c..f14af16 100644 --- a/wifi_ap.h +++ b/wifi_ap.h @@ -47,16 +47,22 @@ void sendStatsPage(WiFiClient client) { } client.print("
  • High score: "); client.print(user_settings.high_score); client.println("
  • "); client.print("
  • Boss kills: "); client.print(user_settings.boss_kills); client.println("
  • "); + + client.print(""); client.print("

    Adjustable Settings

    "); client.print(""); - client.print(""); + + client.print(""); - client.print(""); @@ -113,34 +119,18 @@ void ap_client_check(){ { String line = String(linebuf); - int start = line.indexOf('=', 0) + 1; - int finish = line.indexOf('H', start)-1; - String val = line.substring(start, finish); - // if it is not numeric, it will convert to 0. - // The constrain functions will make it 0 or the min value + int start = line.indexOf('=', 0); + + char paramCode = line.charAt(start - 1); + + int finish = line.indexOf('H', start+1)-1; + String val = line.substring(start+1, finish); + // if it is not numeric, it will convert to 0. + // The the change_setting function will make sure the range is OK + + change_setting(paramCode, val.toInt()); + - if (strstr(linebuf,"B=") > 0){ // typically look like this "GET /?S=100 HTTP/1.1" - user_settings.led_brightness = constrain(val.toInt(), MIN_BRIGHTNESS, MAX_BRIGHTNESS); - FastLED.setBrightness(user_settings.led_brightness); - settings_eeprom_write(); - } - else if (strstr(linebuf,"S=") > 0){ - //String val = line.substring(start, finish); - user_settings.audio_volume = constrain(val.toInt(), MIN_VOLUME, MAX_VOLUME); - settings_eeprom_write(); - } - else if (strstr(linebuf,"D=") > 0){ - user_settings.joystick_deadzone = constrain(val.toInt(), MIN_JOYSTICK_DEADZONE, MAX_JOYSTICK_DEADZONE); - settings_eeprom_write(); - } - else if (strstr(linebuf,"A=") > 0){ - user_settings.attack_threshold = constrain(val.toInt(), MIN_ATTACK_THRESHOLD, MAX_ATTACK_THRESHOLD); - settings_eeprom_write(); - } - else if (strstr(linebuf,"L=") > 0){ - user_settings.lives_per_level = constrain(val.toInt(), MIN_LIVES_PER_LEVEL, MAX_LIVES_PER_LEVEL); - settings_eeprom_write(); - } } // you're starting a new line
    Brightness
    Brightness (10-255)
    Sound Volume