What is the best way to update certain TARGET VARIABLES without having to recompile the entire sketch?
Some examples would be:
Charge controller - target voltages - BULK - FLOAT
Arduino 2560 - END AMPS target
Can the SERIAL MONITOR store new data in either the 2560 EEPROM or the SPI memory?
Can the SERIAL MONITOR call a FUNCTION (subroutine) to do this while the sketch is running?
How would I handle entering the new "targets"?
Right now I am not looking for great detail, just a general can/can't outline.
Thanks in advance for your patience.
I have attached a general outline of the system to be built.
The serial monitor can only send and receive UART serial data.
The usual way to proceed is to develop a serial protocol (and corresponding Arduino code) that reads the serial input, changes variables on the fly, executes functions, possibly with parameters from the input stream, etc.
If you want to take a look at a moderately comprehensive example, this DIY suntracker accepts a variety of control commands from a PC via the USB connection.
The serial control protocol allows it to be steered from the PC or put into one of several automatic operating modes, using a 1 or 2 character command followed by an argument.
General format CC NNNN where CC is one or two lower case ASCII characters, NNNN is an character string, integer or float constant.
Commands:
t hh:mm input local time in hours and minutes (colon required) Conversion from time zone to UTC done internally
d dd/mm set current UTC date (slash required). The present year is initialized in the source code.
ja NNNN jog altitude stepper by NNNN integer steps. NNNN may be preceded by a minus sign
jz NNNN jog azimuth stepper by NNNN integer steps. NNNN may be preceded by a minus sign
sa NNN.NN (set) reset altitude origin and define current altitude position to be NN.NN (degrees, floating point)
sz NNN.NN (set) reset azimuth origin and define current azimuth position to be NN.NN (degrees, floating point)
ma NNN.NN (move) orient altitude of platform to be NNN.NN (degrees floating point)
mz NNN.NN (move) orient azimuth of platform to be NNN.NN (degrees floating point)
a (autotrack) track sun in heliostat mode from this point in time forward. The code will not track when the sun is below the horizon.
you can store the initial values of variables in EEPROM and use commands executed at run time to change them using commands you write.
for example, i have code for a model RR that runs on several different nodes. I don't hard code the node addresses requiring separate compiles for each node, i store the address in eeprom, which is read at startup and have commands to update the EEPROM values.
so as i added each node, i ran a command to updates its address and there reset the board.
i have configure command files
CS _ssid NETGEAR33;_pass xxxxx;_host Cumberland;U
code reads a command up to the semicolon and processes it. CS puts the board into a mode for processing commands to update the variables in RAM. those cmds are _ssid, _pass and _host. The U cmd updates the EEPROM with corresponding RAM variables and reboots
My most recent solution to this problem was an encoder to set values and a small OLED display for feedback then store the setpoint to a file (used RPI Pico).
Iâve also used the serial monitor to do it but that doesnât work as well for non technical users.
I use Serial Monitor, in combination with EEPROM, constantly. Here's the start menu from one of my recent efforts, running in Wokwi:
Touch-Toggle Servo Control
Sep 21 2025 File: /tmp/hexi-arduino:avr:mega/sketch/sketch.ino
Signature error - Writing EEPROM
------------------------------------------------------------------------
| ? display (this) user menu ! three error reporting levels |
| A..Z select turnouts 0-25 0..9 select turnouts 26-35 |
| * describe selected turnout & copy designated turnout |
| = test position ~ swap home/thrown positions |
| \ decrease speed by 1 / increase speed by 1 |
| [ decrease position by 1 ] increase position by 1 |
| { decrease position by 5 } increase position by 5 |
| ( set home position ) set thrown position |
| ^ print table line for code @ print full table for code |
| < write turnout to EEPROM > read turnout from EEPROM |
| # write all EEPROM settings $ recover all EEPROM settings |
| % dump EEPROM content, hex fmt _ erase EEPROM contents |
| dump/erase may be followed by address, length parameters |
------------------------------------------------------------------------
The code allows control of Servos on a Nano(9), Uno(9), or Mega(34), using a single digital input for each device; could be a Touch-Toggle, could be an SPST toggle switch, could be a digital input from another device. Doesnât matter. For me, itâs SPDT controlled, for 7 turnouts in an isolated location on my layout, as most of my turnouts are panel-controlled. I wrote this mostly for an acquaintance on this forum who wanted to use Touch-Toggles, though he seems to have wandered off as heâs not been around for 40 days. Oh well.
Menu commands are mostly obvious; a few are not. For example, ^ emits a line that can be cut-n-pasted directly back into the turnout table in the code, while @ gives all lines needed for that table; handy when one wants to update the code with the final, as-adjusted, positions for all the servos. Servo end positions are adjustable in 1 or 5 us increments(ya, ignores the deadband of the servo). Turnout speeds are adjustable, from ASAP on down to to one us/update(slow), and beyond that, down to 5 us per second(glacial), with many speeds in between. Point movement speed on turnouts is one of those things you donât notice until you notice it, then you canât not notice it.
Iâm throwing this out there mostly to demonstrate what can be done with EEPROMs, and Serial Monitor, with a bit of forethought. Most of it will be meaningless drivel for those not using servos for model railroad turnout point control.
In my example above, typingâ(1405â would result in the home position being set to 1405.
Typing â{{{{{â would result in the current position, whether âhomeâ or âthrownâ, being reduced by 5x5, or 25.
You could implement a similar scheme. One of the caveats is, you have to have a software design that doesnât bury itself in the deep, dark corners; rather, it must be written to be constantly looping, watching for commands from serial as well as doing itâs other duties.
Youâd have to expose your present code, with any warts, for anyone to advise further.
the serial interface will buffer 64 characters. it wil take 64 msec to overflow at 9600 baud. when sending a string from the IDEs serial monitor,it doesn't send anything unitl you press enter. I usually use Serial,readBytesUntil() to read an entire string as soon as it sees a char is available.
usually my command syntax is one or more digits followed by a single letter command. this also allows multiple commands to be concatenated together
Yup. I don't run at 9600, that's dinosaur speed. 115200 makes that huge menu snap, although it gets output so infrequently it doesn't matter.
Beyond that, yep, see the menu - there's very little in there that requires more than a single character.
It's a style. Sure, when dealing with protocols defined by others, you live within their constraints, but when writing the sort of structure described here, short is sweet, brevity is important. The switch statement that that single character command is used in may be as long, or short, as the occasion requires.
setup() and loop() of that particular program:
void setup() {
serialStartup(); //opens port, emits hello message
readEEPROM(); //future - reads settings from EEPROM; function also will write EEPROM, if the EEPROM did not appear to have been configured
serialMenu(); //emits the command menu for configuring turnouts
for (uint8_t i = 0; i < NumTurnouts; i++) { //Now, for each turnout in the array, do the following actions once
pinMode(Turnouts[i].S.buttonPin, INPUT_PULLUP); //make the button input pullup to ensure consistant readings, else the attached turnout will flicker if the input isn't connected.
Turnouts[i].V.state = THROWN; //the button will read as 'HOME' in update, so pre-force the state to 'THROWN' to trigger an update
}
}//end of setup ***************************************************************************
void loop() { //loop automatically cycles through updating all turnouts
serialCheck(); //checks serial for any user commands/requests and executes them
if(updateTurnout(PresTurnout)) PresTurnout = (PresTurnout + 1) % NumTurnouts; //if done, updateTurnout returns true; move to next turnout to be updated
UpdateBuiltinLED(LEDDuration); //changes the onboard LED if it's time; having a flashing indicator that the software isn't 'stuck' somewhere is useful, so I usually put it in
}//end of loop ****************************************************************************
serialCheck() is where the command action happens.
In what I've shown, nothing prevents the user from typing many characters before hitting enter; as I mentioned, five { will successively reduce a position by 5, 5 times. Similarly, given single char turnout selects, one could type
A{B{C{D{E{F{G{H{I{J{K{L{
to reduce the current position of the first 12 servos by 5 us each. Similarly, any sensible combination of characters in that menu can be entered by the user.