initial useable status

This commit is contained in:
2026-07-18 16:28:15 +02:00
parent 88a0e83578
commit d4c50abb70
2 changed files with 613 additions and 1 deletions
+550
View File
@@ -0,0 +1,550 @@
// DCCMFL1616 - DIY DCC Multifunction Loco decoder
// licensed under GPL v3
//
// based on NMRA Dcc Multifunction Motor Decoder Demo by Alex Shepherd
//
// This firmware requires these Arduino Libraries:
//
// 1) The NmraDcc Library from: http://mrrwa.org/download/
//
// These libraries can be found and installed via the Arduino IDE Library Manager
//
// Functionality implemented:
// - motor control (speed & direction) with start and maximum PWM values defined in CV2 & CV5
// - integrated acceleration / deceleration profile controlled by CV3 & CV4
// - exterieur lighting (F0 white head lights, F1 red rear lights, in combination with F5 white head and rear lights) in regards to driving direction
// - defined default Decoder address: 3 according to NMRA & NEM standard
// - factory reset by setting CV8 to 255
// - debugging on UART (TX) can be actived (may slow down reaction of the decoder to updated controls)
//
// Functionality NOT implemented yet:
// analog DC driving mode --> DCC-timeout?
//
#include <NmraDcc.h>
// Uncomment any of the lines below to enable debug messages for different parts of the code
//#define DEBUG_FUNCTIONS
//#define DEBUG_SPEED
//#define DEBUG_PWM
//#define DEBUG_DCC_ACK
//#define DEBUG_DCC_MSG
//#define DEBUG_DCC_RESET
#if defined(DEBUG_FUNCTIONS) or defined(DEBUG_SPEED) or defined(DEBUG_PWM) or defined(DEBUG_DCC_ACK) or defined(DEBUG_DCC_MSG) or defined(DEBUG_DCC_RESET)
#define DEBUG_PRINT
#endif
// This is the default DCC Address
#define DEFAULT_DECODER_ADDRESS 3
// default Version
#define DEFAULT_VERSION_ID 1
// This section defines the Arduino UNO Pins to use --> for testing purposes; not used in production
#ifdef __AVR_ATmega328P__
// Define the Arduino input Pin number for the DCC Signal
#define DCC_PIN 2
#define LED_PIN_WHITE_FRONT 5
#define LED_PIN_WHITE_REAR 6
#define LED_PIN_RED_FRONT 12
#define LED_PIN_RED_REAR 13
#define LED_PIN_CABIN 9
#define MOTOR_PIN_1 11
#define MOTOR_PIN_2 10
// This section defines the Arduino ATTiny1616/1626/3216/3226 Pins to use
#elif defined __AVR_ATtinyxy6__
// Define the Arduino input Pin number for the DCC Signal
#define DCC_PIN PIN_PA1
#define LED_PIN_WHITE_FRONT PIN_PB0
#define LED_PIN_WHITE_REAR PIN_PB1
#define LED_PIN_RED_FRONT PIN_PA5
#define LED_PIN_RED_REAR PIN_PC0
#define LED_PIN_CABIN PIN_PA7
#define MOTOR_PIN_1 PIN_PA3
#define MOTOR_PIN_2 PIN_PA4
#else
#error "Unsupported CPU, you need to add another configuration section for your CPU"
#endif
// Some global state variables
byte FN_0_4_state = 0;
byte FN_5_8_state = 0;
byte FN_9_12_state = 0;
byte FN_13_20_state = 0;
byte FN_21_28_state = 0;
// if cabin LED are connected to white LED pins pull cabin LED pin low
bool invert_cabin_light_logic = true;
uint8_t newDirection = 0;
uint8_t lastDirection = 0;
//speed variables
uint8_t newSpeed = 0;
uint8_t targetSpeed = 0;
uint32_t speedChangeTime = 0;
uint8_t numSpeedSteps = SPEED_STEP_128;
uint8_t currentPwm = 0;
uint8_t targetPwm = 0;
uint8_t lastPwm = 0;
uint8_t accRate = 0;
uint8_t decRate = 0;
uint8_t vStart;
uint8_t vHigh;
// Structure for CV Values Table
struct CVPair
{
uint16_t CV;
uint8_t Value;
};
// CV Addresses we will be using according to NMRA 9.2.2 Table 1
#define CV_VSTART 2
#define CV_ACC_RATE 3
#define CV_DEC_RATE 4
#define CV_VHIGH 5
// Default CV Values Table
CVPair FactoryDefaultCVs [] =
{
// The CV Below defines the Short DCC Address
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, DEFAULT_DECODER_ADDRESS},
// Three Step Speed Table
{CV_VSTART, 120},
{CV_VHIGH, 255},
//Acceleration and deceleration; defaults to 0 (deactivated)
{CV_ACC_RATE, 0},
{CV_DEC_RATE, 0},
// CV7 Manufacturer Version ID
{ CV_VERSION_ID, DEFAULT_VERSION_ID},
// CV8 --> Decoder Reset? (set CV to 255 to initiate factory reset)
{CV_MANUFACTURER_ID, MAN_ID_DIY},
// These two CVs define the Long DCC Address
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, CALC_MULTIFUNCTION_EXTENDED_ADDRESS_MSB(DEFAULT_DECODER_ADDRESS)},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, CALC_MULTIFUNCTION_EXTENDED_ADDRESS_LSB(DEFAULT_DECODER_ADDRESS)},
// ONLY uncomment 1 CV_29_CONFIG line below as approprate
// {CV_29_CONFIG, 0}, // Short Address 14 Speed Steps
{CV_29_CONFIG, CV29_F0_LOCATION}, // Short Address 28/128 Speed Steps
// {CV_29_CONFIG, CV29_EXT_ADDRESSING | CV29_F0_LOCATION}, // Long Address 28/128 Speed Steps
};
NmraDcc Dcc ;
uint8_t FactoryDefaultCVIndex = 0;
// This call-back function is called when a CV Value changes so we can update CVs we're using
void notifyCVChange( uint16_t CV, uint8_t Value)
{
switch(CV)
{
case CV_VSTART:
vStart = Value;
break;
case CV_VHIGH:
vHigh = Value;
break;
case CV_ACC_RATE:
accRate = Value;
break;
case CV_DEC_RATE:
decRate = Value;
break;
}
}
#ifdef DEBUG_DCC_RESET
void notifyDccReset(uint8_t hardReset) {
Serial.printf(F("notifyDccReset: %6s.\n"), hardReset ? "HARD" : "NORMAL");
}
#endif
void notifyCVResetFactoryDefault()
{
// Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset
// to flag to the loop() function that a reset to Factory Defaults needs to be done
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
#ifdef DEBUG_DCC_RESET
Serial.println("Factory reset");
#endif
};
// This call-back function is called whenever we receive a DCC Speed packet for our address
void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps )
{
#ifdef DEBUG_SPEED
Serial.print("notifyDccSpeed: Addr: ");
Serial.print(Addr,DEC);
Serial.print( (AddrType == DCC_ADDR_SHORT) ? "-S" : "-L" );
Serial.print(" Speed: ");
Serial.print(Speed,DEC);
Serial.print(" Steps: ");
Serial.print(SpeedSteps,DEC);
Serial.print(" Dir: ");
Serial.println( (Dir == DCC_DIR_FWD) ? "Forward" : "Reverse" );
#endif
newDirection = Dir;
newSpeed = Speed;
numSpeedSteps = SpeedSteps;
};
// This call-back function is called whenever we receive a DCC Function packet for our address
void notifyDccFunc(uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState)
{
#ifdef DEBUG_FUNCTIONS
Serial.print("notifyDccFunc: Addr: ");
Serial.print(Addr,DEC);
Serial.print( (AddrType == DCC_ADDR_SHORT) ? 'S' : 'L' );
Serial.print(" Function Group: ");
Serial.print(FuncGrp,DEC);
#endif
/* Liste der Funktionen aus NEM 608 Betriebsart 1 (eine Ebene) / List of functions according to NEM608 operation mode 1 (one layer)
Funk | Traktionsart | Dampf | Verbrennung | Elektrisch | SchaltFunktion | Bemerkung
| Kategorie | | | | |
F0 | Beleuchtung | Fahrtrichtung vorwärts / rückwärts | Fahrtrichtung vorwärts / rückwärts | Fahrtrichtung vorwärts / rückwärts | ein / aus |
F1 | Beleuchtung | Rückwärtiges Licht | Rückwärtiges Licht / Schlusslicht | Rückwärtiges Licht / Schlusslicht | ein / aus |
F2 | Betrieb | Achtungspfiff | Achtungspfiff / Signalhorn | Achtungspfiff | Moment | Pfiff oder Horn entsprechend der Ausrüstung. Länge des Pfiffs / Horns wird von der Zeit der Betätigung der Taste bestimmt.
F3 | Geräusche | Stand, Anfahren, Fahren, Bremsen | Anlassen, Motor(en), Stand, Anfahren, Fahren, Bremsen, Abschalten Motor(en) | Stand, Anfahren, Fahren, Bremsen | ein / aus |
F4 | Betrieb | Entkuppeln | Entkuppeln | Entkuppeln |Moment |
F5 | Betrieb | Rangiergang | Rangiergang | Rangiergang | ein / aus | F0 und F1 schalten Beleuchtung ein / aus
F6 | Beleuchtung | Führerstand | Führerstand | Führerstand | ein / aus | Bei zwei Führerständen entsprechend der Fahrtrichtung
F7 | Beleuchtung | Feuerbüchse | Motorraum- / Innenbeleuchtung | Motorraum- / Innenbeleuchtung | ein / aus |
F8 | Auf- /Abrüsten | Dampferzeuger | Abgaserzeuger | Pantograph | ein / aus bzw. auf / ab | Bei zwei Pantographen entsprechend der Fahrtrichtung
F9 | Geräusche | Luftpumpe | Kompressor | Kompressor | ein / aus |
*/
switch(FuncGrp) {
case FN_0_4:
FN_0_4_state = FuncState;
#ifdef DEBUG_FUNCTIONS
Serial.print(" FuncState: ");
Serial.print(FuncState);
Serial.print(" FN 0: ");
Serial.print((FuncState & FN_BIT_00) ? 1 : 0);
Serial.print(" FN 1: ");
Serial.print((FuncState & FN_BIT_01) ? 1 : 0);
Serial.print(" FN 2: ");
Serial.print((FuncState & FN_BIT_02) ? 1 : 0);
Serial.print(" FN 3: ");
Serial.print((FuncState & FN_BIT_03) ? 1 : 0);
Serial.print(" FN 4: ");
Serial.print((FuncState & FN_BIT_04) ? 1 : 0);
#endif
break;
case FN_5_8:
FN_5_8_state = FuncState;
#ifdef DEBUG_FUNCTIONS
Serial.print(" FuncState: ");
Serial.print(FuncState);
Serial.print(" FN 5: ");
Serial.print((FuncState & FN_BIT_05) ? 1 : 0);
Serial.print(" FN 6: ");
Serial.print((FuncState & FN_BIT_06) ? 1 : 0);
Serial.print(" FN 7: ");
Serial.print((FuncState & FN_BIT_07) ? 1 : 0);
Serial.print(" FN 8: ");
Serial.print((FuncState & FN_BIT_08) ? 1 : 0);
#endif
break;
case FN_9_12:
FN_9_12_state = FuncState;
#ifdef DEBUG_FUNCTIONS
Serial.print(" FuncState: ");
Serial.print(FuncState);
Serial.print(" FN 9: ");
Serial.print((FuncState & FN_BIT_09) ? 1 : 0);
Serial.print(" FN 10: ");
Serial.print((FuncState & FN_BIT_10) ? 1 : 0);
Serial.print(" FN 11: ");
Serial.print((FuncState & FN_BIT_11) ? 1 : 0);
Serial.print(" FN 12: ");
Serial.print((FuncState & FN_BIT_12) ? 1 : 0);
#endif
break;
case FN_13_20:
FN_13_20_state = FuncState;
#ifdef DEBUG_FUNCTIONS
Serial.print(" FuncState: ");
Serial.print(FuncState);
Serial.print(" FN 13: ");
Serial.print((FuncState & FN_BIT_13) ? 1 : 0);
Serial.print(" FN 14: ");
Serial.print((FuncState & FN_BIT_14) ? 1 : 0);
Serial.print(" FN 15: ");
Serial.print((FuncState & FN_BIT_15) ? 1 : 0);
Serial.print(" FN 16: ");
Serial.print((FuncState & FN_BIT_16) ? 1 : 0);
Serial.print(" FN 17: ");
Serial.print((FuncState & FN_BIT_17) ? 1 : 0);
Serial.print(" FN 18: ");
Serial.print((FuncState & FN_BIT_18) ? 1 : 0);
Serial.print(" FN 19: ");
Serial.print((FuncState & FN_BIT_19) ? 1 : 0);
Serial.print(" FN 20: ");
Serial.print((FuncState & FN_BIT_20) ? 1 : 0);
#endif
break;
case FN_21_28:
FN_21_28_state = FuncState;
#ifdef DEBUG_FUNCTIONS
Serial.print(" FuncState: ");
Serial.print(FuncState);
Serial.print(" FN 21: ");
Serial.print((FuncState & FN_BIT_21) ? 1 : 0);
Serial.print(" FN 22: ");
Serial.print((FuncState & FN_BIT_22) ? 1 : 0);
Serial.print(" FN 23: ");
Serial.print((FuncState & FN_BIT_23) ? 1 : 0);
Serial.print(" FN 24: ");
Serial.print((FuncState & FN_BIT_24) ? 1 : 0);
Serial.print(" FN 25: ");
Serial.print((FuncState & FN_BIT_25) ? 1 : 0);
Serial.print(" FN 26: ");
Serial.print((FuncState & FN_BIT_26) ? 1 : 0);
Serial.print(" FN 27: ");
Serial.print((FuncState & FN_BIT_27) ? 1 : 0);
Serial.print(" FN 28: ");
Serial.print((FuncState & FN_BIT_28) ? 1 : 0);
#endif
break;
default:
break;
}
#ifdef DEBUG_FUNCTIONS
Serial.println();
#endif
}
// This call-back function is called whenever we receive a DCC Packet and message-debugging is activated
#ifdef DEBUG_DCC_MSG
void notifyDccMsg( DCC_MSG * Msg)
{
Serial.print("notifyDccMsg, Number of Preamble Bits: ") ;
Serial.print (Msg->PreambleBits);
Serial.print("; Data Bytes: ");
for(uint8_t i = 0; i < Msg->Size; i++)
{
Serial.print(Msg->Data[i], HEX);
Serial.write(' ');
}
Serial.println();
}
#endif
// This call-back function is called by the NmraDcc library when a DCC ACK needs to be sent
// Calling this function should cause an increased 60ma current drain on the power supply for 6ms to ACK a CV Read
// So we will just turn the motor on for 8ms and then turn it off again.
// Pay attention to your control station, as it may limit power consumption on the programming track
// (e.g. EX-CommandStation needs the command <D PROGBOOST> sent before programming to allow a
// power consumption of more than 250mA and ACK working properly)
void notifyCVAck(void)
{
#ifdef DEBUG_DCC_ACK
Serial.println("notifyCVAck") ;
#endif
digitalWrite(MOTOR_PIN_1, HIGH);
digitalWrite(MOTOR_PIN_2, LOW);
delay( 8 );
digitalWrite(MOTOR_PIN_1, LOW);
digitalWrite(MOTOR_PIN_2, LOW);
}
void setup()
{
#ifdef DEBUG_PRINT
Serial.begin(115200);
uint8_t maxWaitLoops = 255;
while(!Serial && maxWaitLoops--)
delay(20);
Serial.println("DCCMFL1616 Debugging interface");
Serial.print("Decoder-Address: ");
Serial.println(Dcc.getCV(CV_MULTIFUNCTION_PRIMARY_ADDRESS), DEC);
#endif
// Setup the Pins for the Fwd/Rev LED for Function 0 Headlight
pinMode(LED_PIN_WHITE_FRONT, OUTPUT);
pinMode(LED_PIN_WHITE_REAR, OUTPUT);
pinMode(LED_PIN_RED_FRONT, OUTPUT);
pinMode(LED_PIN_RED_REAR, OUTPUT);
pinMode(LED_PIN_CABIN, OUTPUT);
// Setup the Pins for the Motor H-Bridge Driver
pinMode(MOTOR_PIN_1, OUTPUT);
pinMode(MOTOR_PIN_2, OUTPUT);
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
// Interrupt Number for the Arduino Pin number, which reduces confusion.
#ifdef digitalPinToInterrupt
Dcc.pin(DCC_PIN, 0);
#else
Dcc.pin(0, DCC_PIN, 1);
#endif
Dcc.init( MAN_ID_DIY, DEFAULT_VERSION_ID, FLAGS_MY_ADDRESS_ONLY | FLAGS_AUTO_FACTORY_DEFAULT, 0 );
// Uncomment to force CV Reset to Factory Defaults; usually not needed
// notifyCVResetFactoryDefault();
// Read the current CV values
vStart = Dcc.getCV(CV_VSTART);
vHigh = Dcc.getCV(CV_VHIGH);
accRate = Dcc.getCV(CV_ACC_RATE);
decRate = Dcc.getCV(CV_DEC_RATE);
}
void loop()
{
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
Dcc.process();
// Handle Speed changes
if(targetSpeed != newSpeed) {
targetSpeed = newSpeed;
if (newSpeed <= 1) {
targetPwm = 0;
}
else {
// Calculate PWM value in the range 1..255
uint8_t vScaleFactor;
vScaleFactor = ((vHigh > 1) && (vHigh > vStart)) ? vHigh - vStart : 255 - vStart;
uint8_t modSpeed = newSpeed - 1;
uint8_t modSteps = numSpeedSteps - 1;
targetPwm = (uint8_t) vStart + modSpeed * vScaleFactor / modSteps;
#ifdef DEBUG_PWM
Serial.print("New Speed: vStart: ");
Serial.print(vStart);
Serial.print(" vHigh: ");
Serial.print(vHigh);
Serial.print(" modSpeed: ");
Serial.print(modSpeed);
Serial.print(" vScaleFactor: ");
Serial.print(vScaleFactor);
Serial.print(" modSteps: ");
Serial.print(modSteps);
Serial.print(" targetPwm: ");
Serial.println(targetPwm);
#endif
}
lastPwm = currentPwm;
speedChangeTime = millis();
}
if(targetPwm > currentPwm) {
currentPwm = (accRate == 0) ? targetPwm : lastPwm + floor( (vHigh - vStart) * (millis() - speedChangeTime) / (accRate * 0.896 * 1000) + 0.5);
if (currentPwm < vStart) {
currentPwm = vStart;
lastPwm = vStart;
}
else if (currentPwm > targetPwm) currentPwm = targetPwm;
#ifdef DEBUG_PWM
Serial.print("increase speed; accRate: ");
Serial.print(accRate);
Serial.print(" currentPwm: ");
Serial.print(currentPwm);
Serial.println();
#endif
}
else if(targetPwm < currentPwm) {
currentPwm = (decRate == 0) ? targetPwm : lastPwm - floor( (vHigh - vStart) * (millis() - speedChangeTime) / (decRate * 0.896 * 1000) + 0.5);
if (currentPwm < targetPwm) currentPwm = targetPwm;
if (currentPwm < vStart) currentPwm = 0;
#ifdef DEBUG_PWM
Serial.print("reduce Speed; decRate: ");
Serial.print(decRate);
Serial.print(" currentPwm: ");
Serial.print(currentPwm);
Serial.println();
#endif
}
analogWrite(MOTOR_PIN_1, newDirection ? currentPwm : 0);
analogWrite(MOTOR_PIN_2, newDirection ? 0 : currentPwm);
/*
* lighting
*/
//F0 & F5 white LEDs front
digitalWrite(LED_PIN_WHITE_FRONT, ( FN_0_4_state & FN_BIT_00 & (newDirection << 4 | FN_5_8_state << 4 ) ) ? HIGH : LOW);
#ifdef DEBUG_FUNCTIONS
Serial.print("whithe LEDs front: ");
Serial.println( ( FN_0_4_state & FN_BIT_00 & (newDirection << 4 | FN_5_8_state << 4 ) ) ? 1 : 0);
#endif
//F0 & F5 white LEDs rear
digitalWrite(LED_PIN_WHITE_REAR, ( FN_0_4_state & FN_BIT_00 & (~(newDirection << 4) | FN_5_8_state << 4 ) ) ? HIGH : LOW);
#ifdef DEBUG_FUNCTIONS
Serial.print("whithe LEDs rear: ");
Serial.println( ( FN_0_4_state & FN_BIT_00 & (~(newDirection << 4) | FN_5_8_state << 4 ) ) ? 1 : 0);
#endif
//F1 red LEDs front
digitalWrite(LED_PIN_RED_FRONT, ( FN_0_4_state & FN_BIT_00 & ~(newDirection << 4) & FN_0_4_state << 4 & ~(FN_5_8_state << 4) ) ? HIGH : LOW);
#ifdef DEBUG_FUNCTIONS
Serial.print("RED LEDs front: ");
Serial.println( ( FN_0_4_state & FN_BIT_00 & ~(newDirection << 4) & FN_0_4_state << 4 & ~(FN_5_8_state << 4) ) ? 1 : 0);
#endif
//F1 red LEDs rear
digitalWrite(LED_PIN_RED_REAR, ( FN_0_4_state & FN_BIT_00 & newDirection << 4 & FN_0_4_state << 4 & ~(FN_5_8_state << 4) ) ? HIGH : LOW);
#ifdef DEBUG_FUNCTIONS
Serial.print("RED LEDs rear: ");
Serial.println( ( FN_0_4_state & FN_BIT_00 & newDirection << 4 & FN_0_4_state << 4 & ~(FN_5_8_state << 4) ) ? 1 : 0);
#endif
//F6 cabin light (standard: only works if white LED front/rear active)
digitalWrite(LED_PIN_CABIN, (FN_5_8_state & FN_BIT_06 & invert_cabin_light_logic << 1) ? LOW : HIGH );
#ifdef DEBUG_FUNCTIONS
Serial.print("CABIN LEDs: ");
Serial.println( (FN_5_8_state & FN_BIT_06 & invert_cabin_light_logic << 1) ? LOW : HIGH );
#endif
// Handle resetting CVs back to Factory Defaults
if( FactoryDefaultCVIndex && Dcc.isSetCVReady())
{
FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array
Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV, FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
#ifdef DEBUG_DCC_RESET
Serial.print("Factory reset: ");
Serial.print("CV");
Serial.print(FactoryDefaultCVs[FactoryDefaultCVIndex].CV);
Serial.print(" = ");
Serial.println(FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
#endif
}
}
+63 -1
View File
@@ -1,3 +1,65 @@
# DCCMFL-Firmware
Firmware to all our DCC Multi-Functional Locomotive Controllers.
## Prerequisits
- Arduino IDE 2
- megaTinyCore library: can be installed from Arduino IDE boards manager by adding this URL to the "Additional boards manager URLs": http://drazzy.com/package_drazzy.com_index.json or for manual install you can find the git-repository here: [https://github.com/SpenceKonde/megaTinyCore](https://github.com/SpenceKonde/megaTinyCore)
- NmraDcc library: can be installed from Arduino IDE library manager or from the git-repository at [https://github.com/mrrwa/NmraDcc](https://github.com/mrrwa/NmraDcc)
- Arudino Uno or Mega (or a compatible clone) with [JTAG2UPDI programmer](https://github.com/ElTangas/jtag2updi) burned onto it
- some jupmer wires to connect the decoder to the Arduino Uno
## Compilation
### DCCMFL1616 series - ATtiny1616 based decoders
- In the Arduino IDE go to the Tools-menu and select Board -> megaTinyCore -> ATtiny3226/3216/1626/1616/1606/826/816/806/426/416/406
- In the Arduino IDE go to the Tools-menu and select BOD-voltage -> 4.2V (20MHz or less)
- In the Arduino IDE go to the Tools-menu and select Chip -> ATtiny1616
- In the Arduino IDE go to the Tools-menu and select Clock -> 20 MHz internal
- To build the sketch go to Sketch-menu and select Verify Compile
- Wait for the firmware to compile
- After successful compilation you are ready to [upload](#upload) the firmware to your decoder
## Upload
### DCCMFL1616 series - Connect the decoder with the Arduino
- disconnect decoder and the Arduino UNO from any power sources (USB, power supply, track power, ...)
- Connect the GND pin of the decoder programming-interface with the GND pin on the Arduino
- Connect VCC pin on the decoder with the 5V pin on the Arduino
- connect the UDPI pin on the decoder with digital pin 6 on the Arduino
- connect the Arduino to your computer
#### Upload firmware
- In the Arduino IDE go to the Tools-menu and select Port -> Serial Port of the connected Arduino
- In the Arduino IDE go to the Tools-menu and select Programmer -> jtag2updi
- In the Arduino IDE go to the Sketch-menu and select Upload Using Programmer
- Wait for the firmware to compile and upload to the decoder
- Disconnect your Arduino from your commputer
- Disconnect your decoder from the Arduino
## Setup decoder
- Assemble your decoder on your loco
- Put your loco onto the programming track and activate the programming track power
- Programm your decoder using your DCC programming tools (Standard-DCC-Address is 3 but it might not be properly assigned when the decoder is flashed for the first time)
## Functionaly implemented
- motor control (speed & direction) with start and maximum PWM values defined in CV2 & CV5
- integrated acceleration / deceleration profile controlled by CV3 & CV4
- exterieur lighting (F0 white head lights, F1 red rear lights, in combination with F5 white head and rear lights) in regards to driving direction
- direction related cabin light
- defined default Decoder address: 3 (according to NMRA & NEM standard)
- factory reset by setting CV8 to 255
- debugging on UART (TX) can be activated (may slow down reaction of the decoder to updated controls)
## Functionality planned but NOT implemented yet:
- analog DC driving mode --> DCC-timeout?
## Adapting the firmware to your needs
Just as the electronics design, the firmware is free to use and modify. So if you need any specific functions driven by the decoder you can easily add this to the firmware.
Pretty much all of the firmware (other than the libraries) is found in the DCCMFL1616.ino sketch. The firmware is based on an example of the NmraDcc library. It was heavily altered though to realize a much more functional decoder. There are plenty of comments in the sketch so you should be able to understand how it is working if you have at least some basic programming skills.
So you added a new feature to the firmware? Why don't you let us know about it. Create a fork of the [official repository](https://git.schauaus.at/Fahrzeugsteuerungen/DCCMFL-Firmware.git), add your changes and leave us a message on the fedivers (@modellbau@schauaus.at).
If we think your feature may be useful to others as well, we will test it and probably add it to the official repository eventually. Credits will be granted to you in this case of course!
## Feature request
In case you do not have the programming skills needed and you would like to see an additional feature added to the standard-firmware feel free to reach out to us on the fediverse (@modellbau@schauaus.at) and tell us about what you would like to see added.
There is no guaranty though that or when a new feature may be added.