Eszközdekóder Arduino Nanoval
Ahogy a DCC bevezető részében említettük, a rendszer fontos jellemzője az egyszerűség.
Ez azért is fontos, mert a járművekbe telepített dekóderek mérete kulcsfontosságú, egy modellben az elektronikának a lehető legkisebb helyet kell elfoglalnia.
Egy Arduino Nano mérete miatt nem alkalmas arra, hogy járművekbe kerüljön. Arra viszont, hogy eszközdekóderként használva megismerkedjünk a DCC működésével, tökéletesen megfelel.
Ráadásul, az Arduinohoz rengeteg kiegészítő és modul érhető el (pl. szervó vezérlő), amelyek megkönnyítik a fejlesztést.
A hardware
🔧 Hozzávalók
- Arduino Nano (vagy kompatibilis)
- 1N4148 dióda
- 1 kΩ ellenállás
- 6N137 optocsatoló
- Csatlakozó (opcionális, hogy a sínjelet csatlakoztathassuk)
... és így néz ki a valóságban (itt egy kicst már módosítottunk, hozzáadtuk a címkiválasztó gombot is, erről később még lesz szó):
A kapcsolási rajzot az alábbi ábra mutatja:
A software
A kódot - az érthetőség kedvéért - a lehető legegyszerűbben próbáljuk megvalósítani, ezért az Arduino egyik LED-jét fogjuk vezérelni.
Így a hardware és a software is a lehető legkevesebb elemből áll.
🔧 Hozzávalók
- Számítógép :)
- USB C kábel
- Arduino IDE fejlesztő környezet
Válasszuk ki a megfelelő portot az "Eszközök" menüben.
... és a megfelelő boardot az "Eszközök" menüben.
Arduino kód
Másoljuk be az alábbi kódot az Arduino IDE-be.
Mielőtt feltöltenénk az eszközre, állítsuk be a dekóder címét. Ezt a 20-as sorban találjuk. A 0-t kell kicserélni a kívánt címre.
Ezután csatlakoztassuk az Arduino-t a számítógéphez, és töltsük fel a kódot.
📄 dcc_arduino.ino
#include "Arduino.h"
#define PIN_DCC_IN 2
#define PIN_BUILTIN_LED 13
#define BITARRAY_LEN 145
#define BYTEARRAY_LEN BITARRAY_LEN / 8
static bool interruptOccured = false;
static unsigned long interruptTime;
static unsigned long lastTime;
static bool preambleCompleted = false;
static byte preambleCounter = 0;
static byte bitArray[BITARRAY_LEN];
static byte byteArray[BYTEARRAY_LEN];
static byte bitCounter = 0;
static char byteCounter = 0;
static int interruptOn = RISING;
static int decoderAddress = 0; // Roco Address-1
void interruptHandler() {
interruptOccured = true;
interruptTime = micros();
}
void setup() {
pinMode(PIN_DCC_IN, INPUT_PULLUP);
pinMode(PIN_BUILTIN_LED, OUTPUT);
attachInterrupt(digitalPinToInterrupt(PIN_DCC_IN),interruptHandler, interruptOn);
}
void loop() {
packetParse();
}
void fillByteArray() {
byteCounter=0;
byte mask=0x80;
byteArray[byteCounter]=0;
for(int i=0;i < bitCounter;i++) {
if (i % 9 != 8) {
byteArray[byteCounter] = bitArray[i] ? byteArray[byteCounter] | mask : byteArray[byteCounter] & (~mask);
mask >>= 1;
} else {
mask=0x80;
byteCounter++;
byteArray[byteCounter]=0;
}
}
byteCounter++;
}
void processPacket() {
if (checkCrc()) {
int absAddress = ((byteArray[0] & 0x7F) | (((~byteArray[1]) & 0x70) << 2)) << 3 | (byteArray[1] & 0x7);
int address = absAddress >> 1;
int side = absAddress & 1;
int state = (byteArray[1] >> 3) & 1;
if (address == decoderAddress) {
digitalWrite(PIN_BUILTIN_LED,state);
}
}
}
bool checkCrc() {
fillByteArray();
byte crc=0;
for(int i=0;i < byteCounter-1;i++) crc ^= byteArray[i];
return (crc == byteArray[byteCounter-1]);
}
void packetParse() {
if(interruptOccured) {
int elapsedTime = interruptTime - lastTime;
if(elapsedTime > 10) {
if (elapsedTime > 128 && elapsedTime < 180) { // Invalid Bit, let's change interrupt
preambleCompleted = false;
interruptOn = interruptOn == RISING ? FALLING : RISING;
detachInterrupt(digitalPinToInterrupt(PIN_DCC_IN));
attachInterrupt(digitalPinToInterrupt(PIN_DCC_IN),interruptHandler, interruptOn);
} else {
bool thisBit = elapsedTime < 129;
if (preambleCompleted) {
bitArray[bitCounter] = thisBit;
if ((bitCounter == BITARRAY_LEN-1) || ((bitCounter > 25) && thisBit && ((bitCounter % 9) == 8))) {
if (bitArray[0] && !bitArray[1] && bitArray[9]) processPacket(); // Accessory packet
preambleCompleted = false;
}
bitCounter++;
} else {
if (thisBit) preambleCounter++;
else {
if (preambleCounter > 11) {
preambleCompleted = true;
bitCounter=0;
}
preambleCounter = 0;
}
}
}
}
lastTime = interruptTime;
interruptOccured = false;
}
}