From 8a849fd732ebc6f729a93f3f814d30edb8eba4f0 Mon Sep 17 00:00:00 2001 From: uvos Date: Mon, 31 Jan 2022 21:10:52 +0100 Subject: [PATCH] add support for turnouts --- CMakeLists.txt | 2 +- item.cpp | 2 +- item.h | 3 +- main.cpp | 200 ++++++++++++++++++++++++++++++++++++++++++------- turnout.cpp | 38 ++++++++++ turnout.h | 22 ++++++ 6 files changed, 238 insertions(+), 29 deletions(-) create mode 100644 turnout.cpp create mode 100644 turnout.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 38c98b7..a2c7a20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ set(PORT_SPEED "57600" CACHE STRING "Serial Port Speed") set(PROGRAMMER "stk500v1" CACHE STRING "Programmer Type") set(COMPILE_FLAGS "" CACHE STRING "Additional Compiler Flags") -set(SRC_FILES main.cpp serial.cpp train.cpp item.cpp) +set(SRC_FILES main.cpp serial.cpp train.cpp item.cpp turnout.cpp) # Compiler suite specification set(CMAKE_C_COMPILER /usr/bin/avr-gcc) diff --git a/item.cpp b/item.cpp index 9dea2c0..4aada35 100644 --- a/item.cpp +++ b/item.cpp @@ -74,7 +74,7 @@ void Item::sendAddress(uint8_t address) } -void Item::sendRawAddr(const uint16_t address, const uint16_t data) +void Item::sendRawAddr(const uint8_t address, const uint16_t data) { for(uint8_t j = 0; j < SEND_COUNT; j++) { diff --git a/item.h b/item.h index 400d2b2..b645750 100644 --- a/item.h +++ b/item.h @@ -1,3 +1,4 @@ +#pragma once #include "writepin.h" #include #include @@ -38,7 +39,7 @@ public: Item(const uint8_t address); void sendRaw(const uint16_t data); - static void sendRawAddr(const uint16_t address, const uint16_t data); + static void sendRawAddr(const uint8_t address, const uint16_t data); void setAddress(const uint8_t address); uint8_t getAddress(); diff --git a/main.cpp b/main.cpp index f1a2a6f..30c135a 100644 --- a/main.cpp +++ b/main.cpp @@ -10,6 +10,7 @@ #include "bitrep.h" #include "ringbuffer.h" #include "staticvector.h" +#include "turnout.h" #define COMMAND_BUFFER_SIZE 64 #define SNPRINTF_BUFFER_SIZE 128 @@ -18,6 +19,7 @@ char buffer[SNPRINTF_BUFFER_SIZE]; SVector trains; +SVector turnouts; bool autoff = true; bool powerIsOn = true; @@ -53,23 +55,32 @@ void timer0InterruptEnable(const bool enable) void save_state() { cli(); - EEPROM_write_char( 0, trains.count() ); - EEPROM_write_char( 1, autoff ); + EEPROM_write_char( 0, trains.count()); + EEPROM_write_char( 1, autoff); + EEPROM_write_char( 2, trains.maxSize()); + EEPROM_write_char( 3, turnouts.count()); + EEPROM_write_char( 4, turnouts.maxSize()); for(uint16_t i = 0; i < trains.count(); i++) { EEPROM_write_char( i*2+32, trains[i].getAddress()); EEPROM_write_char( i*2+32+1, trains[i].getFunctionMask()); } + for(uint16_t i = 0; i < turnouts.count(); i++) + { + EEPROM_write_char( i*2+32+trains.maxSize()*2, turnouts[i].getAddress()); + EEPROM_write_char( i*2+32+1+trains.maxSize()*2, turnouts[i].getSubaddress()); + } sei(); } void restore_state() { uint8_t trainCount = EEPROM_read_char(0); + uint8_t turnoutCount = EEPROM_read_char(3); autoff = EEPROM_read_char(1); trains.clear(); - if(trainCount > trains.maxSize() ) + if(trainCount > trains.maxSize() || trains.maxSize() != EEPROM_read_char(2) ) { for(uint16_t i = 0; i < EPPROM_SIZE; i++) EEPROM_write_char(i, 0); @@ -78,6 +89,8 @@ void restore_state() { for(uint8_t i = 0; i < trainCount; i++) trains.push_back(Train(EEPROM_read_char(32+i*2), EEPROM_read_char(32+1+i*2))); + for(uint8_t i = 0; i < turnoutCount; i++) + turnouts.push_back(Turnout(EEPROM_read_char(32+i*2+trains.maxSize()*2), EEPROM_read_char(32+1+i*2+trains.maxSize()*2))); } } @@ -86,12 +99,16 @@ inline static void printHelp(Serial* serial) serial->write_p(PSTR("Available Commands: \n\ help : Show this prompt.\n\ train add [address] [functionmask] : Add train.\n\ - train delete : Delete last train.\n\ + train [nn] delete : Delete last train.\n\ train list : Print list of saved trains.\n\ train [nn] s(top) : Stop nth train.\n\ train [nn] s(peed) [sp] : Set nth train speed.\n\ train [nn] function [x] : Toggle x'ed fuction on train n.\n\ train [nn] r(everse) : Reverse train n.\n\ + turnout add [address] [subaddress] : Add a turnout\n\ + turnout list : List turnouts\n\ + turnout set [left/right] : Set turnout direction\n\ + turnout delete [nn] : Delete Turnout\n\ stop : stop all trains\n\ power off : power off the rail\n\ power on : power on the rail\n\ @@ -100,6 +117,133 @@ inline static void printHelp(Serial* serial) erase : Erase epprom.\n")); } +void setPower(bool on) +{ + if(powerIsOn != on) + { + powerIsOn = on; + + if(on) + { + Train::setOutput(Train::LOW); + _delay_ms(100); + timer0InterruptEnable(true); + } + else + { + timer0InterruptEnable(false); + Train::setOutput(Train::OFF); + } + } +} + +int turnoutDispatch(char* inBuffer, Serial* serial) +{ + if( strcmp(inBuffer, "add") == 0 ) + { + char* token = strtok(NULL, " "); + uint8_t address = 0; + if(token != NULL) + address = strtol(token, nullptr, 10); + if(address != 0 && turnouts.remainingCapacity() > 0) + { + uint8_t subaddress = 0; + + token = strtok(NULL, " "); + if(token != NULL) + subaddress = strtol(token, nullptr, 10); + + turnouts.push_back(Turnout(address, subaddress)); + + uint8_t size = snprintf(buffer, SNPRINTF_BUFFER_SIZE, "TUNROUT saved! NUMBER: %u ADDRESS: %u SUBADDRESS: %u\n", + turnouts.count()-1, + address, + turnouts.back().getSubaddress()); + serial->write(buffer, size); + + save_state(); + return 0; + } + serial->write_p(PSTR("Usage: turnout add [address] [subaddress]")); + } + else if( strcmp(inBuffer, "list") == 0 ) + { + serial->write_p(PSTR("Turnouts:\n")); + for(uint8_t i = 0; i < turnouts.count(); i++) + { + snprintf(buffer, SNPRINTF_BUFFER_SIZE, "NUMBER: %u ADDRESS: %u SUBADDRESS: %u CURRENT PACKET: %x DIRECTION: %u\n", + i, turnouts[i].getAddress(), turnouts[i].getSubaddress(), + turnouts[i].getPacket(), turnouts[i].getDirection()); + serial->write(buffer, SNPRINTF_BUFFER_SIZE); + } + serial->putChar('\n'); + return 0; + } + else + { + uint8_t id = strtol(inBuffer, nullptr, 10); + if(id < turnouts.count() ) + { + setPower(true); + char* token = strtok(NULL, " "); + if(token != NULL && strcmp(token, "set") == 0 ) + { + char* boolToken = strtok(NULL, " "); + if(token != NULL && boolToken != NULL) + { + bool direction = (strcmp(boolToken, "right") == 0); + turnouts[id].setDirection(direction); + serial->write_p(PSTR("Set turnout direction ")); + serial->write(direction ? "right\n" : "left\n"); + return 0; + } + } + else if(token != NULL && strcmp(token, "raw") == 0 ) + { + token = strtok(NULL, " "); + serial->flush(); + if(token != NULL) + { + cli(); + uint16_t i = strtol(token, nullptr, 16 ); + snprintf(buffer, SNPRINTF_BUFFER_SIZE, "SENDING: %x to %x\n", i, turnouts[id].getAddress()); + serial->write(buffer, strlen(buffer)); + while(!serial->dataIsWaiting()) + { + turnouts[id].sendRaw(i); + sei(); + _delay_ms(20); + cli(); + } + serial->write_p(PSTR("Finished\n")); + serial->flush(); + sei(); + return 0; + } + } + else if(token != NULL && strcmp(token, "delete") == 0) + { + turnouts.erase(id); + serial->write_p(PSTR("Turnout: ")); + serial->write(id); + serial->write_p(PSTR(" deleted\n")); + save_state(); + return 0; + } + else + { + serial->write_p(PSTR("Not a valid command\n")); + return -1; + } + } + else + { + serial->write_p(PSTR("Id out of range.\n")); + return -2; + } + } + return -3; +} int trainDispatch(char* inBuffer, Serial* serial) { @@ -136,7 +280,7 @@ int trainDispatch(char* inBuffer, Serial* serial) serial->write_p(PSTR("Trains:\n")); for(uint8_t i = 0; i < trains.count(); i++) { - snprintf(buffer, SNPRINTF_BUFFER_SIZE, "NUMBER: %u ID: %u CURRENT PACKET: %x SPEED: %i FUNCTIONS: %s FUNCTIONMASK: %s\n", + snprintf(buffer, SNPRINTF_BUFFER_SIZE, "NUMBER: %u ADDRESS: %u CURRENT PACKET: %x SPEED: %i FUNCTIONS: %s FUNCTIONMASK: %s\n", i, trains[i].getAddress(), trains[i].getLastPacket(), trains[i].getSpeed(), bit_rep[trains[i].getFunctions() & 0x0F], bit_rep[trains[i].getFunctionMask() & 0x0F]); @@ -171,13 +315,7 @@ int trainDispatch(char* inBuffer, Serial* serial) uint8_t id = strtol(inBuffer, nullptr, 10); if(id < trains.count() ) { - if(powerIsOn == false) - { - powerIsOn = true; - Train::setOutput(Train::LOW); - _delay_ms(100); - timer0InterruptEnable(true); - } + setPower(true); char* token = strtok(NULL, " "); if( token != NULL && (strcmp(token, "speed") == 0 || strcmp(token, "s") == 0) ) { @@ -203,7 +341,8 @@ int trainDispatch(char* inBuffer, Serial* serial) } else if(token != NULL && strcmp(token, "probe") == 0 ) { - for(uint16_t j = 0; j < 1024; j++) + serial->flush(); + for(uint16_t j = 0; j < 1024 && !serial->dataIsWaiting(); j++) { trains[id].sendRaw(j); snprintf(buffer, SNPRINTF_BUFFER_SIZE, "TRYING: %x\n", j); @@ -213,6 +352,7 @@ int trainDispatch(char* inBuffer, Serial* serial) cli(); } sei(); + serial->flush(); return 0; } else if(token != NULL && strcmp(token, "raw") == 0 ) @@ -245,12 +385,14 @@ int trainDispatch(char* inBuffer, Serial* serial) trains[id].stop(); return 0; } - else if( strcmp(inBuffer, "delete") == 0) + else if( token != NULL && strcmp(token, "delete") == 0) { trains.erase(id); serial->write_p(PSTR("Train: ")); serial->write(id); serial->write_p(PSTR(" deleted\n")); + save_state(); + return 0; } else { @@ -271,9 +413,7 @@ int powerDispatch(char* token, Serial* serial) { if(strcmp(token, "off") == 0) { - timer0InterruptEnable(false); - Train::setOutput(Train::OFF); - powerIsOn = false; + setPower(false); return 0; } else if( strcmp(token, "on") == 0) @@ -282,8 +422,7 @@ int powerDispatch(char* token, Serial* serial) { trains[i].setSpeed(0); } - Train::setOutput(Train::LOW); - timer0InterruptEnable(true); + setPower(true); return 0; } else if(strcmp(token, "auto") == 0) @@ -322,12 +461,18 @@ void serialDispatch(Serial* serial) { int ret = -1; char* token = strtok(buffer, " "); - if(length > 4 && (strcmp(token, "train") == 0 || strcmp(token, "t") == 0 )) + if(strcmp(token, "train") == 0 || strcmp(token, "t") == 0 ) { token = strtok(NULL, " "); if(token != NULL) ret = trainDispatch(token, serial); } + else if(strcmp(token, "turnout") == 0) + { + token = strtok(NULL, " "); + if(token != NULL) + ret = turnoutDispatch(token, serial); + } else if(strncmp(token, "erase", 4) == 0) { for(uint16_t i = 0; i < EPPROM_SIZE; i++) EEPROM_write_char(i, 0); @@ -420,17 +565,20 @@ int main() serial.write_p(PSTR("TrainController v0.5 starting\n")); - uint8_t trainToResend = 0; + uint8_t itemToResend = 0; while(true) { - if(resendEvent && trains.count() > 0) + if(resendEvent && (trains.count() > 0 || turnouts.count())) { timer0InterruptEnable(false); - trains[trainToResend].sendData(); - trainToResend++; - if(trains.count() <= trainToResend) - trainToResend = 0; + if(itemToResend < trains.count()) + trains[itemToResend].sendData(); + else if(itemToResend < trains.count() + turnouts.count()) + turnouts[itemToResend-trains.count()].sendData(); + itemToResend++; + if(trains.count()+turnouts.count() <= itemToResend) + itemToResend = 0; resendEvent = false; timer0InterruptEnable(true); } diff --git a/turnout.cpp b/turnout.cpp new file mode 100644 index 0000000..21f137b --- /dev/null +++ b/turnout.cpp @@ -0,0 +1,38 @@ +#include "turnout.h" + +Turnout::Turnout(uint8_t address, uint8_t subaddress): Item(address), _subaddress(subaddress) +{ +} + +void Turnout::setDirection(uint8_t direction) +{ + _direction = direction; + sendData(); +} + +uint8_t Turnout::getDirection() +{ + return _direction; +} + +uint8_t Turnout::getSubaddress() +{ + return _subaddress; +} + +uint16_t Turnout::getPacket() +{ + uint16_t data = 0; + data |= (_direction << 9) | (_direction << 8); + for (uint8_t i = 0; i < 2; ++i) + { + uint8_t bit = (_subaddress & (1 << i)) >> i; + data |= (bit << (7-i*2)) | (bit << (6-i*2)); + } + return data; +} + +void Turnout::sendData() +{ + sendRaw(getPacket()); +} diff --git a/turnout.h b/turnout.h new file mode 100644 index 0000000..662079f --- /dev/null +++ b/turnout.h @@ -0,0 +1,22 @@ +#pragma once +#include "item.h" +#include + +class Turnout: public Item +{ +public: + static constexpr uint8_t LEFT = 0; + static constexpr uint8_t RIGHT = 1; + +private: + uint8_t _direction = LEFT; + uint8_t _subaddress; + +public: + Turnout(uint8_t address, uint8_t subaddress); + void setDirection(uint8_t direction); + uint8_t getDirection(); + uint8_t getSubaddress(); + uint16_t getPacket(); + void sendData(); +};