BeneluxSpoor.net forum
Vraag en antwoord => Elektronica en analoog => Topic gestart door: dickkoning op 19 August 2015, 14:05:44
-
Hallo
op dit forum heeft Paul Smits het onderwerp "Analoge modelbaansturing met arduino" opgestart
Geïnspireerd door zijn voorbeeld heb ik ook een analoge testbaan met arduino besturing gebouwd
In dit draadje wil ik eea gaan beschrijven en hopelijk met jullie input verder gaan uitbouwen
Uiteraard is alles "Work in Progress"
Momenteel is het testbaantje nog super simpel
(https://images.beneluxspoor.net/bnls/plan_18.jpg) (https://images.beneluxspoor.net/bnls/plan_18.jpg)
(https://images.beneluxspoor.net/bnls/20150819_124351.jpg) (https://images.beneluxspoor.net/bnls/20150819_124351.jpg)
Ook de arduino hardware is eenvoudig van opzet
(https://images.beneluxspoor.net/bnls/20150819_124319.jpg) (https://images.beneluxspoor.net/bnls/20150819_124319.jpg)
(https://images.beneluxspoor.net/bnls/20150819_124328_2.jpg) (https://images.beneluxspoor.net/bnls/20150819_124328_2.jpg)
De hardware bestaat uit een oude fleischmann trafo, een arduino nano, een LM298 motorshield, een ULN 2803 tbv wisselsturing en een 2 tal TRCT5000 lichtsluisjes.
5 volt voor de arduino etc wordt gegenereerd door een dc-dc converter omdat de gelijkgerichte wisselspanning van de transformator toch in de orde van 18-20 Volt zit. Genoeg om de ingebouwde regulators van de arduino en het het motorshield op te blazen. :( :( :(
Indien gewenst zal ik het schema wel op papier zetten maar eea is erg standaard
Software en een filmpje van het resultaat komen in de volgende post :P
-
De software
//Keyboard Controls:
// 1 -Motor 1 Left // 2 -Motor 1 Stop // 3 -Motor 1 Right
// 4 -Motor 2 Left // 5 -Motor 2 Stop // 6 -Motor 2 Right
// Declare L298N Dual H-Bridge Motor Controller directly since there is not a library to load.
// motorsturing
const int dir1PinA = 2; // motor driver
const int dir2PinA = 3; // motor driver
const int speedPinA = 9; // Needs to be a PWM pin to be able to control motor speed
int speed = 100; // default speed
bool richting = true; // richting true = forward false = backward
// IR sensor
const int led1 = 7; // de pin van de IR led
const int led2 = 8;
const int sensor1 = A1; //de pin van de 1e ir sensor
const int sensor2 = A2; // de pin van de 2e ir sensor
int waarde1 = 0;
int waarde2 = 0;
// wisselschakelaar
const int wissel1R = 11; // wissel 1
const int wissel1L = 10;
// potmeter
const int pot = A0; //de pin van de potmeter
int potspeed = 0; //de ingestelde snelheid mbv de potmeter
// switches
bool potmeter = true;
int teller = 0;
// timers
long snelheidTimer = 0; // timer voor optrekken afremmen pauze routines
long sensorTimer1 = 0; // timer voor sensor1 timeout
long sensorTimer2 = 0; // timer voor sensor2 timeout
// status variabelen
int stationState = 0; // 0 geen actie 1 remmen 2 wachten 3 optrekken
int blokState = 0; // 0 geen actie 1 remmen 2 wachten 3 optrekken
void setup() {
// Setup runs once per reset
// initialize serial communication @ 9600 baud:
Serial.begin(9600);
//Define Pins
pinMode(dir1PinA, OUTPUT);
pinMode(dir2PinA, OUTPUT);
pinMode(speedPinA, OUTPUT);
pinMode (sensor1, INPUT_PULLUP);
pinMode (sensor2, INPUT_PULLUP);
pinMode (led1, OUTPUT);
pinMode (led2, OUTPUT);
pinMode (pot, INPUT);
pinMode (wissel1R, OUTPUT);
pinMode (wissel1L, OUTPUT);
// start forward met default speed
analogWrite(speedPinA, speed); //Sets speed variable via PWM
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
}
void loop() {
// Initialize the Serial interface:
if (Serial.available() > 0) {
keyboardControl(Serial.read());
}
if (richting) { // de trein rijdt vooruit
if (leesSensor1()) { // indien sensor 1 positief :rem de trein af, pauzeer , start de trein op (station) schakel de pot meter even uit
potmeter = false;
stationState = 1;
}
if (leesSensor2() ) { // de trein gaat vooruit tel bv 3 rondjes, zet dan de wissel om naar station
++teller;
if (teller > 3) { // eventueel testen of het station leeg is
wissel1Rechts();
teller = 0;
}
}
}
else { // de trein rijdt achteruit
if (leesSensor2()) { // de trein passeert de sensor achteruit, remt, wissel gaat om en trein gaat weer vooruit
blokState = 1;
potmeter = false;
}
}
if (stationState > 0) Station();
if (blokState > 0) Blok();
if (potmeter) leesPot();
}
bool Optrekken() {
bool b = false; // b=false : trein trekt niet verder op (snelheid bereikt)
if (speed < potspeed) {
b = true;
if (millis() - snelheidTimer > 100) {
speed = speed + 10;
if (speed > potspeed) speed=potspeed;
snelheidTimer = millis();
analogWrite(speedPinA, speed);
}
}
return b;
}
bool Remmen() {
bool b = false; // b=false : trein remt niet verder af (snelheid bereikt)
if (speed > 0) {
b = true;
if (millis() - snelheidTimer > 100) {
speed = speed - 10;
if (speed <0) speed=0;
snelheidTimer = millis();
analogWrite(speedPinA, speed);
}
}
return b;
}
void Station() {
bool b = true;
switch (stationState) {
case 1:
b = Remmen(); // de functie remmen vermindert de snelheid van de trein tot 0, return= false als snelheid 0 is
if (!b) {
stationState = 2;
wissel1Links();
}
break;
case 2:
if (millis() - snelheidTimer > 10000) {
if (richting == true) { // draai de rijrichting om
analogWrite(speedPinA, 0);
digitalWrite(dir1PinA, HIGH);
digitalWrite(dir2PinA, LOW);
richting = false;
}
// eventuele toets om te kijken of de trein kan vertrekken ( volgende blok vrij bv)
snelheidTimer = millis();
stationState = 3;
wissel1Rechts();
}
break;
case 3:
b = Optrekken();
if (!b) {
stationState = 0;
potmeter = true;
}
break;
}
}
void Blok() {
bool b = true;
switch (blokState) {
case 1:
b = Remmen(); // de functie remmen vermindert de snelheid van de trein tot 0, return= false als snelheid 0 is
if (!b) {
blokState = 2;
wissel1Links();
}
break;
case 2:
if (millis() - snelheidTimer > 1000) {
if (richting == false) { // draai de rijrichting om
analogWrite(speedPinA, 0);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
richting = true;
}
// eventuele toets om te kijken of de trein kan vertrekken ( volgende blok vrij bv)
snelheidTimer = millis();
blokState = 3;
}
break;
case 3:
b = Optrekken();
if (!b) {
blokState = 0;
potmeter = true;
}
break;
}
}
void wissel1Rechts() {
digitalWrite (wissel1R, 1);
delay(150);
digitalWrite (wissel1R, 0);
}
void wissel1Links() {
digitalWrite (wissel1L, 1);
delay(150);
digitalWrite (wissel1L, 0);
}
void leesPot() {
potspeed = map(analogRead(pot), 0, 1023, 0, 255);
if ((speed - potspeed) > 3 || (potspeed - speed) > 3) {
speed = potspeed;
analogWrite(speedPinA, speed);
//Serial.print("Motor 1 Speed "); Serial.println(speed);
// Serial.println(" ");
}
}
bool leesSensor1() {
bool b = false;
if (millis() - sensorTimer1 > 1000) { // na activatie van de sensor is die 1 sec niet meer te triggeren
digitalWrite (led1, HIGH);
waarde1 = analogRead(sensor1);
delay(1);
digitalWrite (led1, LOW);
waarde2 = analogRead(sensor1);
if ((waarde1 - waarde2) > 150) { // arbitraire waarde fine tunen nodig
b = true;
sensorTimer1 = millis();
}
}
return b;
}
bool leesSensor2() {
bool b = false;
if (millis() - sensorTimer2 > 1000) { // na activatie van de sensor is die 1 sec niet meer te triggeren
digitalWrite (led2, HIGH);
waarde1 = analogRead(sensor2);
delay(1);
digitalWrite (led2, LOW);
waarde2 = analogRead(sensor2);
if ((waarde1 - waarde2) > 150) { // arbitraire waarde fine tunen nodig
b = true;
sensorTimer2 = millis();
}
}
return b;
}
void keyboardControl(int inByte) {
switch (inByte) {
case '1': // Motor 1 Forward
analogWrite(speedPinA, speed); //Sets speed variable via PWM
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
richting = true;
Serial.println("Motor 1 Forward");
Serial.println(" ");
break;
case '2': // Motor 1 Stop (Freespin)
analogWrite(speedPinA, 0);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
richting = true;
Serial.println("Motor 1 Stop");
Serial.println(" ");
break;
case '3': // Motor 1 Reverse
analogWrite(speedPinA, speed);
digitalWrite(dir1PinA, HIGH);
digitalWrite(dir2PinA, LOW);
richting = false;
Serial.println("Motor 1 Reverse");
Serial.println(" ");
break;
case '+': // Motor 1 speed +
if (speed < 255) ++speed;
analogWrite(speedPinA, speed);
Serial.print("Motor 1 Speed "); Serial.println(speed);
Serial.println(" ");
break;
case '-': // Motor 1 speed +
if (speed > 200) --speed;
analogWrite(speedPinA, speed);
Serial.print("Motor 1 Speed "); Serial.println(speed);
Serial.println(" ");
break;
case 'l': // wissel links
wissel1Links();
Serial.print("Wissel links");
Serial.println(" ");
break;
case 'r': // wissel rechts
wissel1Rechts();
Serial.print("Wissel rechts");
Serial.println(" ");
break;
}
}
In opzet een erg simpele dienstregeling:
trein stopt bij station, keert om, gaat naar hoofdspoor, keert om, rijdt 3 rondjes, gaat naar station ???
Volgende fase 2 treintjes, 2 stations
-
Leuk dat je je experiment wilt delen!
Wat is voor jou de reden dat je met Arduino aan het experimenteren bent geweest?
Gr, Bert
-
Hoi Dick,
Altijd leuk om andere bezig te zien. Mag ik je alleen toch een paar tips geven?
Allereerst het maken van iets logischere variabelenamen. Het is logischer om een eits langere naam te gebruiken waarvan je gelijk snapt wat het doet dan een korte naam. Iets als waarde1 is nogal een niets zeggende variabele. Nu weet jij precies wat het doet maar stop je even en ga je over een maandje weer verder kan zo'n variabele toch echt wel wat moeite kosten.
Kom ik gelijk aan volgende punt, declareer variabele waar ze nodig zijn. Je gebruikt de variabele waarde1 en waarde2 alleen voor de sensor uitlezing, waarom declareer je ze niet daar? Dan is de variabel naam al een stuk logischer.
Gaat eigenlijk vooral om het slimmer schrijven van functies. Je hebt nu een aantal functies die eigenlijk een volledige copy past van elkaar zijn. (leesSensor 1 en leessenson2 of wissel1Links en wissel2Rechts.) Eigenlijk kan je als vuistregel houden als je heel veel dingen schrijft die erg op elkaar lijken er een makkelijkere manier is om het te doen. Voorbeeld voor de sensoren is:
const byte SensorPinnen[] = {A1, A2}; //Pinnen van de lichtsluis sensoren
const byte LedPinnen[] = {7, 8}; //Pinnen van de ledjes voor de lichtsluizen
unsigned int sensorTimers[sizeof(SensorPinnen)];//Timers voor de sensoren (int omdat <65sec)
bool leesSensor(byte nummer){
unsigned int millisNow = millis(); //omdat we intervallen van minder dan 65sec bekijken gaan we naar een uint
int waardes[2];
if (millisNow - sensorTimers[nummer] > 1000) { // na activatie van de sensor is die 1 sec niet meer te triggeren
//Bekijken met led aan en led uit
digitalWrite (LedPinnen[nummer], HIGH);
waardes[0] = analogRead(SensorPinnen[nummer]);
//delay(1); //lijkt me niet nodig
digitalWrite (LedPinnen[nummer], LOW);
waardes[1] = analogRead(SensorPinnen[nummer]);
if ((waardes[0] - waardes[1]) > 150) { // arbitraire waarde fine tunen nodig
sensorTimers[nummer] = millisNow;
return true;
}
}
return false;
}
Op deze manier is het schaalbaar naar oneindig veel sensoren door alleen de pinnen toe te voegen. Er is een functie voor alle sensoren met als input het nummer van de sensor (natuurlijk nu startende bij 0).
Zelfde kan je doen voor wissels. Maar daar zou ik nog iets meer werk in stoppen om van de delay af te komen....
Ook alle dingen voor de motorsturing zou ik in losse functies onder brengen. Dit scheelt weer code als je de motor aan wilt sturen. Een library/classe kan het nog simpeler maken. Je zou hiervoor de AamsTrein library kunnen gebruiken. Ik heb hem even als library online gezet (https://github.com/sseptillion/Septillion_LM298). Ik heb hem eigenlijk niet getest (heb geen LM298 / niet met de scope gekeken ::) ) maar het moet je een idee geven.
Nog klein puntje, alle variabele voor pinnen mogen ook een byte zijn. Dit scheelt weer wat geheugen. Ook vind ik het zelf altijd handig om voor constante (wat je hier prima toepast (y) ) de naam te laten beginnen met een hoofdletter (cont byte ConstantVariableNaam) om me te helpen herinneren dat ik er niet naar kan schrijven.
Ook zou ik voor serial gewoon de volle bak gaan. Dus Serial.begin(115200).
Ik hoop dat je hier wat mee kunt :) Succes verder en ik zla het blijven volgen.
Timo
-
hallo
@Timo: je hebt gelijk, ik ben nog niet zo gewend met array's te werken maar het is natuurlijk veel makkelijker om op te schalen
Gelukkig heeft de nano maar 5 analoge pinnen. Ik neem je commentaar mee in de volgende versie van de code
Harde delay in de aansturing van de wissel heb ik als veiligheid opgenomen ::)
@Bert: ik heb met een Multimaus en de digitale interfaces van Rosoft heel gemakkelijk een DCC baantje kunnen bouwen.
Werkt prima maar is vooral een kwestie van goed lezen wat er in de handleidingen staat
Arduino is een leuke manier om te kijken of het ook anders kan. Hardware is heel makkelijk verkrijgbaar en met wat fantasie knoop je allerlei zaken aan elkaar De uitdaging die ik me zelf gesteld heb is een baantje waarin autonoom 2 treintjes rijden
-
de besturing in actie ;D
https://www.youtube.com/v/HqNdlAGVY90
-
Ik zie niet veel actie. Krijg een boodschap: Meld je aan om deze video te bekijken. Zo krijg je geen hoge kijkcijfers. ;D
-
is ook een erg saai filmpje ;D
zal eea gaan uitzoeken
-
Nu werkt het wel. Inderdaad doodsaai. ;D
-
Leuk leuk, goed begin :)
Vink in YouTube alleen nog ff het beeldstabilisatie effect aan ;)
En harde delay, tja, die gaat later altijd voor problemen zorgen ;)
Timo
-
Hallo
@Timo
De eerste verbeterde versie van de arduino code
Nog niet alle suggesties verwerkt maar dat gaat nog wel komen ::)
Moet nog even wennen aan de array constructies, volgende stap de library
// Arduino analoge modelbaan besturing vs 1a
// Dick koning augustus 2015
//Keyboard Controls:
// 1 -Motor 1 Left // 2 -Motor 1 Stop // 3 -Motor 1 Right
// 4 -Motor 2 Left // 5 -Motor 2 Stop // 6 -Motor 2 Right
// Declare L298N Dual H-Bridge Motor Controller directly since there is not a library to load.
// motorsturing
const int dir1PinA = 2; // motor driver
const int dir2PinA = 3; // motor driver
const int speedPinA = 9; // Needs to be a PWM pin to be able to control motor speed
int speed = 100; // default speed
bool richting = true; // richting true = forward false = backward
// IR sensor
const byte SensorPinnen[] = {A1, A2}; //Pinnen van de lichtsluis sensoren
const byte LedPinnen[] = {7, 8}; //Pinnen van de ledjes voor de lichtsluizen
// wisselschakelaar
const byte WisselPinnen[] = {10, 11}; // Pinnen van de wisselsturing 1L, 1R etc
// potmeter
const int pot = A0; //de pin van de potmeter
int potspeed = 0; //de ingestelde snelheid mbv de potmeter
// switches
bool potmeter = true;
int teller = 0;
// timers
long snelheidTimer = 0; // timer voor optrekken afremmen pauze routines
unsigned int sensorTimers[sizeof(SensorPinnen)];//Timers voor de sensoren (int omdat <65sec), size off definieert de lengte van de array
// status variabelen
int stationState = 0; // 0 geen actie 1 remmen 2 wachten 3 optrekken
int blokState = 0; // 0 geen actie 1 remmen 2 wachten 3 optrekken
void setup() {
// Setup runs once per reset
// initialize serial communication @ 115200 baud:
Serial.begin(115200);
//Define Pins
pinMode(dir1PinA, OUTPUT);
pinMode(dir2PinA, OUTPUT);
pinMode(speedPinA, OUTPUT);
for ( int i = 0 ; i < sizeof(SensorPinnen); i++) {
pinMode (SensorPinnen[i], INPUT_PULLUP);
pinMode (LedPinnen[i], OUTPUT);
}
for ( int i = 0 ; i < sizeof(WisselPinnen); i++) {
pinMode (WisselPinnen[i], OUTPUT);
}
pinMode (pot, INPUT);
// start forward met default speed
analogWrite(speedPinA, speed); //Sets speed variable via PWM
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
}
void loop() {
// Initialize the Serial interface:
if (Serial.available() > 0) {
keyboardControl(Serial.read());
}
if (richting) { // de trein rijdt vooruit
if (leesSensor(0)) { // indien sensor 1 positief :rem de trein af, pauzeer , start de trein op (station) schakel de pot meter even uit
potmeter = false;
stationState = 1;
}
if (leesSensor(1) ) { // de trein gaat vooruit tel bv 3 rondjes, zet dan de wissel om naar station
teller++;
if (teller > 2) { // eventueel testen of het station leeg is
wissel(1); // wissel R
teller = 0;
}
}
}
else { // de trein rijdt achteruit
if (leesSensor(1)) { // de trein passeert de sensor achteruit, remt, wissel gaat om en trein gaat weer vooruit
blokState = 1;
potmeter = false;
}
}
if (stationState > 0) Station();
if (blokState > 0) Blok();
if (potmeter) leesPot();
}
bool Optrekken() {
bool b = false; // b=false : trein trekt niet verder op (snelheid bereikt)
if (speed < potspeed) {
b = true;
if (millis() - snelheidTimer > 100) {
speed = speed + 5;
if (speed > potspeed) speed = potspeed;
snelheidTimer = millis();
analogWrite(speedPinA, speed);
}
}
return b;
}
bool Remmen() {
bool b = false; // b=false : trein remt niet verder af (snelheid bereikt)
if (speed > 0) {
b = true;
if (millis() - snelheidTimer > 100) {
speed = speed - 5;
if (speed < 0) speed = 0;
snelheidTimer = millis();
analogWrite(speedPinA, speed);
}
}
return b;
}
void Station() {
bool b = true;
switch (stationState) {
case 1:
b = Remmen(); // de functie remmen vermindert de snelheid van de trein tot 0, return= false als snelheid 0 is
if (!b) {
stationState = 2;
wissel(0); // wissel 1 l
}
break;
case 2:
if (millis() - snelheidTimer > 10000) {
if (richting == true) { // draai de rijrichting om
analogWrite(speedPinA, 0);
digitalWrite(dir1PinA, HIGH);
digitalWrite(dir2PinA, LOW);
richting = false;
}
// eventuele toets om te kijken of de trein kan vertrekken ( volgende blok vrij bv)
snelheidTimer = millis();
stationState = 3;
wissel(1); // wissel 1 r
}
break;
case 3:
b = Optrekken();
if (!b) {
stationState = 0;
potmeter = true;
}
break;
}
}
void Blok() {
bool b = true;
switch (blokState) {
case 1:
b = Remmen(); // de functie remmen vermindert de snelheid van de trein tot 0, return= false als snelheid 0 is
if (!b) {
blokState = 2;
wissel(0); // wissel l
}
break;
case 2:
if (millis() - snelheidTimer > 1000) {
if (richting == false) { // draai de rijrichting om
analogWrite(speedPinA, 0);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
richting = true;
}
// eventuele toets om te kijken of de trein kan vertrekken ( volgende blok vrij bv)
snelheidTimer = millis();
blokState = 3;
}
break;
case 3:
b = Optrekken();
if (!b) {
blokState = 0;
potmeter = true;
}
break;
}
}
void wissel(byte n) {
digitalWrite (WisselPinnen[n], 1);
delay(150);
digitalWrite (WisselPinnen[n], 0);
//Serial.Print("Wissel ");Serial.println(n);
}
void leesPot() {
potspeed = map(analogRead(pot), 0, 1023, 0, 255);
if ((speed - potspeed) > 3 || (potspeed - speed) > 3) {
speed = potspeed;
analogWrite(speedPinA, speed);
//Serial.print("Motor 1 Speed "); Serial.println(speed);
}
}
bool leesSensor(byte nummer) {
unsigned int millisNow = millis(); //omdat we intervallen van minder dan 65sec bekijken gaan we naar een uint
int waardes[2];
if (millisNow - sensorTimers[nummer] > 1000) { // na activatie van de sensor is die 1 sec niet meer te triggeren
//Bekijken met led aan en led uit
digitalWrite (LedPinnen[nummer], HIGH);
waardes[0] = analogRead(SensorPinnen[nummer]);
delay(1); //lijkt me niet nodig
digitalWrite (LedPinnen[nummer], LOW);
waardes[1] = analogRead(SensorPinnen[nummer]);
if ((waardes[0] - waardes[1]) > 150) { // arbitraire waarde fine tunen nodig
sensorTimers[nummer] = millisNow;
// Serial.print("Sensor ");Serial.println(nummer);
return true;
}
}
return false;
}
void keyboardControl(int inByte) {
switch (inByte) {
case '1': // Motor 1 Forward
analogWrite(speedPinA, speed); //Sets speed variable via PWM
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
richting = true;
Serial.println("Motor 1 Forward");
Serial.println(" ");
break;
case '2': // Motor 1 Stop (Freespin)
analogWrite(speedPinA, 0);
digitalWrite(dir1PinA, LOW);
digitalWrite(dir2PinA, HIGH);
richting = true;
Serial.println("Motor 1 Stop");
Serial.println(" ");
break;
case '3': // Motor 1 Reverse
analogWrite(speedPinA, speed);
digitalWrite(dir1PinA, HIGH);
digitalWrite(dir2PinA, LOW);
richting = false;
Serial.println("Motor 1 Reverse");
Serial.println(" ");
break;
case '+': // Motor 1 speed +
if (speed < 255) ++speed;
analogWrite(speedPinA, speed);
Serial.print("Motor 1 Speed "); Serial.println(speed);
Serial.println(" ");
break;
case '-': // Motor 1 speed +
if (speed > 200) --speed;
analogWrite(speedPinA, speed);
Serial.print("Motor 1 Speed "); Serial.println(speed);
Serial.println(" ");
break;
case 'l': // wissel links
wissel(0);
Serial.print("Wissel links");
Serial.println(" ");
break;
case 'r': // wissel rechts
wissel(1);
Serial.print("Wissel rechts");
Serial.println(" ");
break;
}
}
@Klaas
de bedoeling is 2 treintjes te besturen met 1 microprocessor. Dat geeft dus zo wie zo kans op een botsing, ergo een veel spannender filmpje. Gaat wel een paar weken duren ;D ;D