Topic: tech dcc pc src prev next
tech dcc pc src > manual.c
#include "manual.h"
#include <stdio.h>
#include <util/delay.h>
#include "cfg.h"
#include "controller.h"
#include "dcc.h"
#include "lcd.h"
#include "led.h"
#include "menu.h"
#include "millis.h"
#include "packet.h"
#include "pins.h"
cfg_address_type choose_locomotive()
{
typedef struct
{
unsigned char index;
cfg_address_type addresses[CFG_LOCO_LIST_LENGTH+1];
cfg_address_type selected;
} settings_state;
settings_state context = {
.index = 0,
.selected = CFG_NULL_ADDR
};
void load_addresses(settings_state *context_)
{
Cfg_Addresses(context_->addresses);
}
load_addresses(&context);
int count(const settings_state *context_)
{
return Cfg_AddressCount(context_->addresses);
}
void button(void *v, const switch_type sw)
{
settings_state *context_ = (settings_state*)v;
if(HERMES_SWITCH_NO(sw) == HERMES_SWITCH_BACK)
Menu_Finish();
if(HERMES_SWITCH_NO(sw) == HERMES_RE_ACW)
{
context_->index =
(unsigned char)((count(context_) + context_->index + 1) % count(context_));
Menu_Redraw();
}
if(HERMES_SWITCH_NO(sw) == HERMES_RE_CW)
{
context_->index =
(unsigned char)((context_->index + 1) % count(context_));
Menu_Redraw();
}
if(HERMES_SWITCH_NO(sw) == HERMES_RE_SEL)
{
context_->selected = context_->addresses[context_->index];
Menu_Finish();
}
}
void draw(void *v)
{
settings_state *context_ = (settings_state*)v;
LCD_DefineHermesChars(HERMES_LCD_CUSTOM_MODE_MENU);
LCD_Clear();
LCD_PutS("Locos");
LCD_PrintListIndex(context_->index + 1, count(context_));
LCD_Move(0, 1);
LCD_PutI((int)context_->addresses[context_->index]);
}
Menu_EventLoop(button, draw, 0, &context);
return context.selected;
}
void manual_mode(bool show_current)
{
enum manual_mode
{
FUNCTION,
ACCESSORY,
END_MANUAL_MODE
};
typedef enum
{
SEQ_POM,
SEQ_SPEED1,
SEQ_F1,
SEQ_F2,
SEQ_LP1,
SEQ_SPEED2,
SEQ_F3,
SEQ_F4,
SEQ_LP2,
SEQ_F5,
END_PACKET_SEQUENCE_TYPE
} packet_sequence_type;
typedef enum
{
LP_SEQ_SPEED,
LP_SEQ_F1,
LP_SEQ_F2,
LP_SEQ_F3,
LP_SEQ_F4,
LP_SEQ_F5,
END_LP_SEQUENCE_TYPE
} lp_sequence_type;
typedef struct
{
// State of each of the locomotive's functions.
functions_type functions;
// Current speed.
unsigned char speed;
// Locomotive speed steps (for transmitting speed and direction packets).
unsigned char speed_steps;
// Current direction.
bool direction;
// Train is stopped. Setting this overrides the speed temporarily.
bool stopped;
// Locomotive address. Set to CFG_NULL_ADDR if this block is not in
// use.
cfg_address_type address;
} loco_control_block;
typedef struct
{
// Locomotives in memory.
cfg_address_type addresses[CFG_LOCO_LIST_LENGTH + 1];
// State of each locomotive's functions. The index is the locomotive's
// index in 'addresses'.
loco_control_block loco_controls[CFG_LOCO_LIST_LENGTH];
// The current index in the locomotive control blocks.
cfg_loco_index_type loco_control_index;
// The current locomotive in the low priority queue (index into
// loco_controls).
cfg_loco_index_type low_priority_index;
// The position in the low priority queue sequence.
lp_sequence_type lp_seq;
// Current locomotive.
cfg_loco loco;
// Number of times the update function has been called.
unsigned long int update_count;
// The next packet type to transmit in sequence.
packet_sequence_type packet_sequence;
// A POM packet to transmit as a high priority.
packet pom_packet;
// Number of times to transmit the POM packet.
unsigned int pom_retransmit;
// Current current.
adc_type current;
// Display current on the LCD.
bool show_current;
// Current display mode (changed by pressing buttons 10-12).
int mode;
// Should the accessory list option be shown?
bool show_accessory_list;
// Accessories assigned to function keys.
cfg_address_type accessories[8];
// Accessories in the 'on' state.
cfg_address_type on_accessories[CFG_ACCESSORY_LIST_LENGTH + 1];
} manual_state;
manual_state context = {
.loco_control_index = 0,
.low_priority_index = 0,
.lp_seq = LP_SEQ_SPEED,
.update_count = 0,
.packet_sequence = (END_PACKET_SEQUENCE_TYPE + 1) % END_PACKET_SEQUENCE_TYPE,
.pom_retransmit = 0,
.current = 0,
.show_current = show_current,
.mode = FUNCTION,
.show_accessory_list = false
};
for(unsigned char i = 0; i < 8; ++i)
context.accessories[i] = CFG_NULL_ADDR;
// Indicate forward direction.
Led_Sel2();
Cfg_Addresses((cfg_address_type*)&(context.addresses));
for(int i = 0; i < CFG_LOCO_LIST_LENGTH; ++i)
{
if(context.addresses[i] == CFG_NULL_ADDR)
{
context.loco_controls[i].address = CFG_NULL_ADDR;
break;
}
context.loco_controls[i].address = context.addresses[i];
// Copy the locomotive's default function states to the control block's
// functions.
cfg_loco loco = Cfg_ReadLoco(context.addresses[i]);
context.loco_controls[i].functions = loco.f_default;
context.loco_controls[i].speed_steps = loco.speed_steps;
// Set the default direction (forward).
context.loco_controls[i].direction = true;
// And the speed (stopped).
context.loco_controls[i].speed = 0;
context.loco_controls[i].stopped = false;
}
context.loco = Cfg_ReadLoco(context.addresses[0]);
// TODO: is show_accessory_list used anywhere? If not, remove this.
{
cfg_address_type accessories[CFG_ACCESSORY_LIST_LENGTH + 1];
Cfg_Accessories(accessories);
if(Cfg_AddressCount(accessories) > 0)
context.show_accessory_list = true;
}
Cfg_ReadAccessoryOn(context.on_accessories);
Cfg_ReadAccessoryHotlist(context.accessories);
if(Cfg_AddressCount((cfg_address_type*)&(context.addresses)) == 0)
{
Menu_Notice("No locomotives");
return;
}
void set_direction_led(manual_state *context_)
{
if(context_->loco_controls[context_->loco_control_index].direction)
Led_Sel2();
else
Led_Sel1();
}
void load_loco(manual_state *context_, cfg_address_type addr)
{
if(context_->loco.address == addr)
return;
// Locomotive configuration can be changed in manual mode.
Cfg_WriteLoco(context_->loco);
context_->loco_control_index = Cfg_IndexInList(
addr,
context_->addresses
);
context_->loco = Cfg_ReadLoco(addr);
set_direction_led(context_);
}
unsigned char max_speed(manual_state *context_)
{
return (
(context_->loco.speed_steps == SPEED_STEPS_128) ? 126 : 26
);
}
void reverse(manual_state *context_)
{
context_->loco_controls[context_->loco_control_index].direction =
!(context_->loco_controls[context_->loco_control_index].direction);
set_direction_led(context_);
}
void accessory_message(cfg_address_type addr, bool state)
{
char buf[17];
snprintf(buf, sizeof(buf), "Acc. %d (%s)", addr, state ? "ON" : "OFF");
Menu_FakeButton(
Menu_TimedNotice(buf, HERMES_ACTIVATION_DISPLAY_MS)
);
}
void momentary_function_message(unsigned char fn_no)
{
char buf[17];
snprintf(buf, sizeof(buf), "Fn. %d (once)", fn_no);
Menu_FakeButton(
Menu_TimedNotice(buf, HERMES_ACTIVATION_DISPLAY_MS)
);
}
void function_message(unsigned char fn_no, bool state)
{
char buf[17];
snprintf(buf, sizeof(buf), "Fn. %d (%s)", fn_no, state ? "ON" : "OFF");
Menu_TimedNotice(buf, HERMES_ACTIVATION_DISPLAY_MS);
}
void activate_accessory(
manual_state *context_,
cfg_address_type acc_no
)
{
if(acc_no == CFG_NULL_ADDR)
{
Menu_Notice("No Accessory");
}
else
{
// Current accessory state.
const bool accessory_state = (
Cfg_IndexInList(acc_no, context_->on_accessories) !=
CFG_NULL_INDEX
);
// Change the accessory state.
if(accessory_state)
Cfg_RemoveFromList(acc_no, context_->on_accessories);
else
Cfg_AddToList(acc_no, context_->on_accessories);
const bool new_accessory_state = !accessory_state;
// Get the correct physical accessory address and output index.
const cfg_address_type accessory_addr =
(cfg_address_type)( ((acc_no - 1) / 4) + 1);
const unsigned char output_index = (unsigned char)(
( ((acc_no - 1) % 4) * 2) +
(new_accessory_state ? 1 : 0)
);
DCC_TransmitRepeat(
Packet_CreateAccessoryBasic(accessory_addr, output_index, true),
HERMES_ACCESSORY_TRANSMISSION_COUNT
);
accessory_message(acc_no, new_accessory_state);
}
}
void activate_function(
manual_state *context_,
unsigned char f_no
)
{
if(f_no == CFG_NULL_FN)
{
/*Menu_Notice("No Function");*/
}
else
{
// Activate this function.
if(context_->loco.f_momentary & (functions_type)(1UL << f_no))
{
// Transmit the 'on' part.
context_->loco_controls[context_->loco_control_index].functions |=
(functions_type)(1UL << f_no);
Controller_TransmitFunctionGroup(
context_->loco.address,
context_->loco_controls[context_->loco_control_index].functions,
Controller_FunctionGroup(f_no),
HERMES_MOMENTARY_FUNCTION_ACTIVATION_COUNT
);
// Display a message which may be cancelled early by the user.
momentary_function_message(f_no);
// Transmit the 'off' part.
context_->loco_controls[context_->loco_control_index].functions &= (functions_type)~(1UL << f_no);
Controller_TransmitFunctionGroup(
context_->loco.address,
context_->loco_controls[context_->loco_control_index].functions,
Controller_FunctionGroup(f_no),
HERMES_MOMENTARY_FUNCTION_DEACTIVATION_COUNT
);
}
else
{
context_->loco_controls[context_->loco_control_index].functions ^=
(functions_type)(1UL << f_no);
Controller_TransmitFunctionGroup(
context_->loco.address,
context_->loco_controls[context_->loco_control_index].functions,
Controller_FunctionGroup(f_no),
HERMES_MOMENTARY_FUNCTION_ACTIVATION_COUNT
);
function_message(
f_no,
context_->loco_controls[context_->loco_control_index].functions &
(functions_type)(1UL << f_no)
);
}
}
}
void button(void *v, const switch_type sw)
{
manual_state *context_ = (manual_state*)v;
// Stop control and exit.
if(HERMES_SWITCH_NO(sw) == HERMES_SWITCH_BACK)
Menu_Finish();
// Choose a locomotive.
if(HERMES_SWITCH_NO(sw) == HERMES_SWITCH_LOCO && HERMES_SWITCH_IS_SHORT(sw))
{
cfg_address_type next_loco = Cfg_NextInList(
context_->addresses,
context_->loco.address
);
load_loco(context_, next_loco);
Menu_Redraw();
}
if(HERMES_SWITCH_NO(sw) == HERMES_SWITCH_LOCO && HERMES_SWITCH_IS_LONG(sw))
{
cfg_address_type prev_loco = Cfg_PrevInList(
context_->addresses,
context_->loco.address
);
load_loco(context_, prev_loco);
Menu_Redraw();
}
// The rotary encoder always controls the speed.
// Actually adjust the speed.
if(HERMES_SWITCH_NO(sw) == HERMES_RE_ACW)
{
if(context_->loco_controls[context_->loco_control_index].direction)
{
if(context_->loco_controls[context_->loco_control_index].speed > 0)
--context_->loco_controls[context_->loco_control_index].speed;
else
reverse(context_);
}
else
{
if(context_->loco_controls[context_->loco_control_index].speed < max_speed(context_))
++context_->loco_controls[context_->loco_control_index].speed;
}
Menu_Redraw();
}
if(HERMES_SWITCH_NO(sw) == HERMES_RE_CW)
{
if(context_->loco_controls[context_->loco_control_index].direction)
{
if(context_->loco_controls[context_->loco_control_index].speed < max_speed(context_))
++context_->loco_controls[context_->loco_control_index].speed;
}
else
{
if(context_->loco_controls[context_->loco_control_index].speed > 0)
--context_->loco_controls[context_->loco_control_index].speed;
else
reverse(context_);
}
Menu_Redraw();
}
// Pressing the rotary encoder stops the locomotive if it is moving or
// reverses the locomotive if it is stationary.
if(HERMES_SWITCH_NO(sw) == HERMES_RE_SEL && HERMES_SWITCH_IS_SHORT(sw))
{
if(context_->loco_controls[context_->loco_control_index].speed == 0)
reverse(context_);
else
context_->loco_controls[context_->loco_control_index].speed = 0;
Menu_Redraw();
}
// Holding the rotary encoder stops all locomotives.
if(HERMES_SWITCH_NO(sw) == HERMES_RE_SEL && HERMES_SWITCH_IS_LONG(sw))
{
for(int i = 0; i < CFG_LOCO_LIST_LENGTH; ++i)
context_->loco_controls[i].speed = 0;
Menu_Redraw();
}
// Long press function button - locomotive POM.
if(
HERMES_SWITCH_NO(sw) == HERMES_SWITCH_FUNCTION &&
HERMES_SWITCH_IS_LONG(sw)
)
{
const cfg_address_type loco_no = choose_locomotive();
if(loco_no == CFG_NULL_ADDR)
return;
const int cv_no = Menu_GetInt("CV No.", -1, HERMES_MAX_CV_NO);
if(cv_no == -1)
return;
const int value = Menu_GetInt("Value", -1, HERMES_MAX_CV_VALUE);
if(value == -1)
return;
context_->pom_packet = Packet_CreatePomLocoWrite(
loco_no,
(cfg_address_type)cv_no,
(unsigned char)value
);
context_->pom_retransmit = 5;
}
// Short press function button - go to function mode, or choose a
// function if already in function mode.
if(
HERMES_SWITCH_NO(sw) == HERMES_SWITCH_FUNCTION &&
HERMES_SWITCH_IS_SHORT(sw)
)
{
if(context_->mode == FUNCTION)
{
// Choose any function to activate now.
const unsigned char fn_no = Controller_FunctionMenu(
&(context_->loco),
&(context_->loco_controls[context_->loco_control_index].functions)
);
activate_function(context_, fn_no);
}
else
{
context_->mode = FUNCTION;
Menu_Redraw();
}
}
// Long press accessory button - offer to program an accessory decoder
// CV on main.
if(
HERMES_SWITCH_NO(sw) == HERMES_SWITCH_ACCESSORY &&
HERMES_SWITCH_IS_LONG(sw)
)
{
const cfg_address_type acc_no =
Controller_AccessoryMenu(context_->on_accessories);
if(acc_no == CFG_NULL_ADDR)
return;
const int cv_no = Menu_GetInt("CV No.", -1, HERMES_MAX_CV_NO);
if(cv_no == -1)
return;
const int value = Menu_GetInt("Value", -1, HERMES_MAX_CV_VALUE);
if(value == -1)
return;
context_->pom_packet = Packet_CreatePomAccWrite(
(cfg_address_type)(((acc_no - 1) / 4) + 1),
(cfg_address_type)cv_no,
(unsigned char)value
);
context_->pom_retransmit = 5;
}
// Short press accessory button - switch to accessory mode or choose an
// accessory to activate if already in accessory mode.
if(
HERMES_SWITCH_NO(sw) == HERMES_SWITCH_ACCESSORY &&
HERMES_SWITCH_IS_SHORT(sw)
)
{
if(context_->mode == ACCESSORY)
{
// Choose any accessory to activate now.
const cfg_address_type acc_no =
Controller_AccessoryMenu(context_->on_accessories);
activate_accessory(context_, acc_no);
}
else
{
// Switch to accessory hotkey mode.
context_->mode = ACCESSORY;
Menu_Redraw();
}
}
// Reprogram one of the buttons 1-8 in function or accessory mode.
if(
HERMES_SWITCH_IS_LONG(sw) &&
HERMES_SWITCH_NO(sw) >= 1 &&
HERMES_SWITCH_NO(sw) <= 8
)
{
if(context_->mode == FUNCTION)
{
if(context_->loco.f_list[HERMES_SWITCH_NO(sw) - 1] == CFG_NULL_FN)
{
const unsigned char fn_no = Controller_FunctionMenu(
&(context_->loco),
&(context_->loco_controls[context_->loco_control_index].functions)
);
if(fn_no != CFG_NULL_INDEX)
// Assign this function to the switch.
context_->loco.f_list[HERMES_SWITCH_NO(sw) - 1] = fn_no;
}
else
{
context_->loco.f_list[HERMES_SWITCH_NO(sw) - 1] = CFG_NULL_FN;
}
}
if(context_->mode == ACCESSORY)
{
if(context_->accessories[HERMES_SWITCH_NO(sw) - 1] == CFG_NULL_ADDR)
{
const cfg_address_type acc_no =
Controller_AccessoryMenu(context_->on_accessories);
context_->accessories[HERMES_SWITCH_NO(sw) - 1] = acc_no;
}
else
{
context_->accessories[HERMES_SWITCH_NO(sw) - 1] = CFG_NULL_ADDR;
}
}
Menu_Redraw();
}
// Activate one of the functions or accessories on buttons 1-8.
if(
HERMES_SWITCH_IS_SHORT(sw) &&
HERMES_SWITCH_NO(sw) >= 1 &&
HERMES_SWITCH_NO(sw) <= 8
)
{
if(context_->mode == FUNCTION)
activate_function(
context_,
context_->loco.f_list[HERMES_SWITCH_NO(sw) - 1]
);
if(context_->mode == ACCESSORY)
activate_accessory(
context_,
context_->accessories[HERMES_SWITCH_NO(sw) - 1]
);
}
}
unsigned char function_button_assigned(manual_state *context_)
{
unsigned char out = 0;
switch(context_->mode)
{
case ACCESSORY:
for(unsigned char i = 0; i < 8; ++i)
{
if(context_->accessories[i] != CFG_NULL_ADDR)
out = (unsigned char)(out | (1 << i));
}
break;
case FUNCTION:
for(unsigned char i = 0; i < 8; ++i)
{
if(context_->loco.f_list[i] != CFG_NULL_FN)
out = (unsigned char)(out | (1 << i));
}
break;
}
return out;
}
void draw(void *v)
{
manual_state *context_ = (manual_state*)v;
LCD_DefineHermesChars(HERMES_LCD_CUSTOM_MODE_OPS);
LCD_Clear();
char function_button_char(unsigned char assigned)
{
switch(assigned)
{
case 0b01:
return HERMES_LCD_CHAR_BUTTON_TOP;
case 0b10:
return HERMES_LCD_CHAR_BUTTON_BOTTOM;
case 0b11:
return HERMES_LCD_CHAR_BUTTON_BOTH;
}
return ' ';
}
const unsigned char button_assigned = function_button_assigned(context_);
const char buttons_tl = function_button_char(button_assigned & 0b11);
const char buttons_bl = function_button_char((button_assigned >> 2) & 0b11);
const char buttons_tr = function_button_char((button_assigned >> 4) & 0b11);
const char buttons_br = function_button_char((button_assigned >> 6) & 0b11);
LCD_PutC(buttons_tl);
LCD_PutS("Loco ");
LCD_PutI((int)context_->loco.address);
switch(context_->mode)
{
case ACCESSORY:
LCD_Move(11, 0);
LCD_PutS("Ac");
break;
case FUNCTION:
{
const int f_count =
Cfg_LocoFunctions(&context_->loco);
if(f_count > 0)
{
LCD_Move((unsigned char)(12 - LCD_DigitCount(f_count)), 0);
LCD_PutS("Fn ");
LCD_PutI(f_count);
}
}
break;
};
LCD_Move(15, 0);
LCD_PutC(buttons_tr);
if(context_->show_current)
{
LCD_Move(9, 0);
LCD_PutI((int)(context_->current));
}
LCD_Move(0, 1);
LCD_PutC(buttons_bl);
// Print the direction and speed in 'large' format (16 characters).
LCD_PutC((context_->loco_controls[context_->loco_control_index].direction) ? 'F' : 'R');
LCD_PutC(' ');
unsigned char hb = 0;
switch(context_->loco_controls[context_->loco_control_index].speed_steps)
{
case SPEED_STEPS_128:
hb = (unsigned char)(context_->loco_controls[context_->loco_control_index].speed / 6);
break;
case SPEED_STEPS_28:
hb = (unsigned char)(context_->loco_controls[context_->loco_control_index].speed);
break;
}
for(unsigned char i = 0; i < 8; ++i)
{
if(hb / 2 > i)
LCD_PutC((char)255);
else if(hb / 2 == i && hb % 2)
LCD_PutC(HERMES_LCD_CHAR_HB);
else
LCD_PutC(' ');
}
LCD_PutC(' ');
LCD_PutI((int)context_->loco_controls[context_->loco_control_index].speed);
LCD_Move(15, 1);
LCD_PutC(buttons_br);
}
// Transmit a single packet from the low priority queue.
// Low priority queue packets are on a per locomotive basis. Once the
// sequence has finished, the next locomotive in the locomotive list will
// be chosen as the low priority queue target before the sequence restarts.
void transmit_lp(manual_state *context_)
{
if(
context_->addresses[context_->low_priority_index] == context_->loco.address ||
context_->lp_seq == LP_SEQ_SPEED
)
// Advance to the next locomotive in the low priority sequence.
context_->low_priority_index = (cfg_loco_index_type)(
(context_->low_priority_index + 1) % Cfg_AddressCount(context_->addresses)
);
// Skip past the current locomotive if it was selected.
if(context_->addresses[context_->low_priority_index] == context_->loco.address)
context_->low_priority_index = (cfg_loco_index_type)(
(context_->low_priority_index + 1) % Cfg_AddressCount(context_->addresses)
);
switch(context_->lp_seq)
{
case LP_SEQ_SPEED:
// All locomotives in the low priority queue should be stopped.
DCC_Transmit(
(context_->loco_controls[context_->low_priority_index].speed_steps == SPEED_STEPS_128) ?
Packet_CreateSpeed128(
context_->addresses[context_->low_priority_index],
context_->loco_controls[context_->low_priority_index].direction,
context_->loco_controls[context_->low_priority_index].speed
) :
Packet_CreateSpeed28(
context_->addresses[context_->low_priority_index],
context_->loco_controls[context_->low_priority_index].direction,
context_->loco_controls[context_->low_priority_index].speed
)
);
break;
case LP_SEQ_F1:
Controller_TransmitFunctionGroup(
context_->addresses[context_->low_priority_index],
context_->loco_controls[context_->low_priority_index].functions,
1,
1
);
break;
case LP_SEQ_F2:
Controller_TransmitFunctionGroup(
context_->addresses[context_->low_priority_index],
context_->loco_controls[context_->low_priority_index].functions,
2,
1
);
break;
case LP_SEQ_F3:
Controller_TransmitFunctionGroup(
context_->addresses[context_->low_priority_index],
context_->loco_controls[context_->low_priority_index].functions,
3,
1
);
break;
case LP_SEQ_F4:
Controller_TransmitFunctionGroup(
context_->addresses[context_->low_priority_index],
context_->loco_controls[context_->low_priority_index].functions,
4,
1
);
break;
case LP_SEQ_F5:
Controller_TransmitFunctionGroup(
context_->addresses[context_->low_priority_index],
context_->loco_controls[context_->low_priority_index].functions,
5,
1
);
break;
case END_LP_SEQUENCE_TYPE:
break;
}
context_->lp_seq = (context_->lp_seq + 1) % END_LP_SEQUENCE_TYPE;
}
void update(void *v)
{
manual_state *context_ = (manual_state*)v;
if(Menu_Current(&(context_->current)) && context_->show_current)
Menu_Redraw();
Controller_CheckOverCurrent(context_->current);
// Transmit the next packet in the queue.
switch(context_->packet_sequence)
{
case SEQ_POM:
if(context_->pom_retransmit > 0)
{
DCC_Transmit(context_->pom_packet);
--context_->pom_retransmit;
}
break;
case SEQ_SPEED1:
case SEQ_SPEED2:
transmit_speed(
&(context_->loco),
(context_->loco_controls[context_->loco_control_index].stopped) ? 0 : (unsigned char)(context_->loco_controls[context_->loco_control_index].speed),
context_->loco_controls[context_->loco_control_index].direction
);
break;
case SEQ_F1:
Controller_TransmitFunctionGroup(
context_->loco.address,
context_->loco_controls[context_->loco_control_index].functions,
1,
1
);
break;
case SEQ_F2:
Controller_TransmitFunctionGroup(
context_->loco.address,
context_->loco_controls[context_->loco_control_index].functions,
2,
1
);
break;
case SEQ_F3:
Controller_TransmitFunctionGroup(
context_->loco.address,
context_->loco_controls[context_->loco_control_index].functions,
3,
1
);
break;
case SEQ_F4:
Controller_TransmitFunctionGroup(
context_->loco.address,
context_->loco_controls[context_->loco_control_index].functions,
4,
1
);
break;
case SEQ_F5:
Controller_TransmitFunctionGroup(
context_->loco.address,
context_->loco_controls[context_->loco_control_index].functions,
5,
1
);
break;
case SEQ_LP1:
case SEQ_LP2:
if(Cfg_AddressCount(context_->addresses) > 1)
transmit_lp(context_);
break;
case END_PACKET_SEQUENCE_TYPE:
break;
}
context_->packet_sequence = (context_->packet_sequence + 1) % END_PACKET_SEQUENCE_TYPE;
}
DCC_Start();
Menu_EventLoop(button, draw, update, &context);
DCC_Stop();
// Save the ordered list of accessories that are 'on'.
Cfg_SaveAccessoryOn(context.on_accessories);
Cfg_SaveAccessoryHotlist(context.accessories);
// Locomotive configuration can be changed in manual mode.
Cfg_WriteLoco(context.loco);
Led_Off();
}