An attempt to simulate Acceleration and Braking
In my previous post, I'd managed to get my Densha-De-Go Dreamcast controller hooked up to my Arduino Mega. Now although this now meant that I had a great way to control my model railroad, it also meant I had to work out how to code a throttle and a brake lever.
The rules
After a few hours of plotting, I had decided on the following system. It involves a 6-position throttle and an 11-position brake. Each 'position' is to have a 'max speed' and 'speed adjust' associated with it.
Notes
- It is to be assumed that if the brake is on, the throttle is automatically disabled
- MAX is 255 on the PWM Throttle (or max voltage from supply)
- At Throttle 0, the train is neither powering nor braking; we will simply slowly decrement the speed
- There is no feedback to know how fast the train is currently travelling
- There are multiple emergency brake points on the throttle, but we don't care about them.
The next table shows my acceleration and braking deltas. This will be a simple addition and subtraction on the current speed.
Lever position | Max Speed | Speed Adjustment |
---|---|---|
Emergency Full | Instant Stop | |
Emergency 5 | -50 | |
Emergency 4 | -30 | |
Emergency 3 | -25 | |
Emergency 2 | -20 | |
Emergency 1 | -10 | |
Brake 9 | -2.4 | |
Brake 8 | -1.8 | |
Brake 7 | -1.2 | |
Brake 6 | -0.8 | |
Brake 5 | -0.4 | |
Brake 4 | -0.2 | |
Brake 3 | -0.1 | |
Brake 2 | -0.05 | |
Brake 1 | -0.025 | |
Throttle 0 | 0 | 0.00 |
Throttle 1 | 55 | +0.25 |
Throttle 2 | 75 | +0.5 |
Throttle 3 | 90 | +1 |
Throttle 4 | 100 | +1.75 |
Throttle 5 | 120 | +2.5 |
And now a better way to represent it.
The code
The table above translates to code quite easily... the goal is to have the lever position values coded into an array and then just select the correct entry. Once determined, the main code loop can then determine how to adjust the current voltage output to the rails.
const int throttle_positions = 21; const int throttle_absolute_maximum_speed = 255; const int throttle_minimum_speed = 20; int current_throttle_position = -1; // 0 is EM FULL. Lever must be moved to EM FULL to begin. float current_speed = 0; float target_speed = 0; int throttle_max_speed[throttle_positions] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //brake positions and Throttle 0 0, 55, 75, 90, 100, 120 }; float throttle_delta[throttle_positions] = { 0.025, 0.05, 0.1, 0.2, 0.4, 0.8, 1.2, 1.8, 2.4, 10, 20, 25, 30, 50, 9999, //brake 0.00 /*coast*/, 0.25, 0.5, 1, 1.75, 2.5 };
We then need to determine the current throttle position. We will make it that, at the start of code execution, the train should not move until the throttle has been reset to EM Full and the throttle at '0'.
#define BRAKE_MASK 0xf0 #define BRAKE_SHIFT 4 #define ACCEL_MASK 0x07 void read_throttle_position() { int accel = packet.data[6] & ACCEL_MASK; int brake = (int)((packet.data[7] & BRAKE_MASK) >> BRAKE_SHIFT); if (current_throttle_position == -1) { //check that we have EM FULL and Neutral if (brake == 15 && accel == 1) { //set the initial '0' (EM FULL) position. current_throttle_position = 0; lcd.clear(); } } else { if (brake != 1) { //1 == "BRAKE 1", if it's higher, we're braking. if (brake > 0) current_throttle_position = brake; } else { //BRAKE == 1 and then we check the throttle //we're accelerating. if ((accel + 15) < 22) current_throttle_position = accel + 15; } } }
And now the main game loop needs to determine the current lever locations and then choose the appropriate action:
void update_speed() { digitalWrite(13, LOW); //make sure we are allowed to go. if (current_throttle_position >= 0) { if (current_speed > throttle_max_speed[current_throttle_position - 1]) { current_speed -= throttle_delta[current_throttle_position - 1]; //braking... don't go negative. if (current_speed < throttle_minimum_speed) current_speed = 0; } else if (current_speed < throttle_max_speed[current_throttle_position - 1]) { //accelerating, so start from minimum speed. if (current_speed < throttle_minimum_speed) current_speed = throttle_minimum_speed; current_speed += throttle_delta[current_throttle_position - 1]; //don't go faster than current throttle max setting. if (current_speed > throttle_max_speed[current_throttle_position - 1]) current_speed = throttle_max_speed[current_throttle_position - 1]; } //set light if we have met max speed for throttle. if (current_speed == throttle_max_speed[current_throttle_position - 1]) digitalWrite(13, HIGH); //output speed to railway. analogWrite(7, current_speed); } else { //flash the LED to alert user to reset controls. delay(200); //delay a little to flash the LED digitalWrite(13, HIGH); delay(200); } }
There's also some code in the main loop to update the 16x2 LCD I've hooked up. Since we need to reset the controller when we start, it'll tell you to do so and afterwards will tell you the current throttle/brake position, current speed and speed delta.
Note that this does not include the full arduino-maple code. Download that here. You will also need the LiquidCrystal library from the Arduino site.
Action shots
Shown is the controller in certain positions. Note that the 'Throttle' positions may show a negative speed delta; this just means that they are no longer accelerating.