302 lines
8.8 KiB
C++
302 lines
8.8 KiB
C++
#include <avr/io.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include "serial.h"
|
|
#include "writepin.h"
|
|
#include "train.h"
|
|
#include "eeprom.h"
|
|
|
|
const char *bit_rep[16] =
|
|
{
|
|
[ 0] = "0000", [ 1] = "0001", [ 2] = "0010", [ 3] = "0011",
|
|
[ 4] = "0100", [ 5] = "0101", [ 6] = "0110", [ 7] = "0111",
|
|
[ 8] = "1000", [ 9] = "1001", [10] = "1010", [11] = "1011",
|
|
[12] = "1100", [13] = "1101", [14] = "1110", [15] = "1111",
|
|
};
|
|
|
|
#define MAX_TRAINS 16
|
|
#define COMMAND_BUFFER_SIZE 64
|
|
#define SNPRINTF_BUFFER_SIZE 64
|
|
|
|
char buffer[SNPRINTF_BUFFER_SIZE];
|
|
|
|
uint16_t storedTrains = 0;
|
|
Train trains[MAX_TRAINS];
|
|
|
|
#define TICK_MAX 255
|
|
uint8_t tick = 0;
|
|
|
|
bool autoff = true;
|
|
bool powerIsOn = true;
|
|
|
|
ISR(TIMER0_OVF_vect)
|
|
{
|
|
if(powerIsOn)
|
|
{
|
|
++tick;
|
|
if(tick == TICK_MAX)
|
|
{
|
|
for(uint16_t i = 0; i < storedTrains; i++) trains[i].resendSpeed();
|
|
if(autoff)
|
|
{
|
|
bool trainsRunning = false;
|
|
for(uint16_t i = 0; i < storedTrains; i++) trainsRunning = trainsRunning || trains[i].getSpeed();
|
|
if(!trainsRunning)
|
|
{
|
|
powerIsOn = false;
|
|
Train::setOutput(Train::OFF);
|
|
}
|
|
}
|
|
tick = 0;
|
|
}
|
|
else
|
|
{
|
|
|
|
Train::setOutput(Train::HIGH);
|
|
_delay_us(200);
|
|
Train::setOutput(Train::LOW);
|
|
}
|
|
}
|
|
}
|
|
|
|
void save_state()
|
|
{
|
|
EEPROM_write_char( 0, storedTrains );
|
|
EEPROM_write_char( 1, autoff );
|
|
for(uint16_t i = 0; i < storedTrains; i++)
|
|
{
|
|
EEPROM_write_char( i+32, trains[i].getAddress());
|
|
}
|
|
|
|
}
|
|
|
|
void restore_state()
|
|
{
|
|
storedTrains = EEPROM_read_char(0);
|
|
autoff = EEPROM_read_char(1);
|
|
if(storedTrains > MAX_TRAINS )
|
|
{
|
|
for(uint16_t i = 0; i < MAX_TRAINS+32; i++) EEPROM_write_char(i, 0);
|
|
storedTrains = 0;
|
|
}
|
|
else for(uint8_t i = 0; i <= storedTrains; i++)
|
|
{
|
|
uint8_t address = EEPROM_read_char(32+i);
|
|
trains[i].setAddress(address);
|
|
}
|
|
}
|
|
|
|
inline static void printHelp(Serial* serial)
|
|
{
|
|
serial->write_p(PSTR("Available Commands: \n\
|
|
help : Show this prompt.\n\
|
|
train add [address] : Add train.\n\
|
|
train delete : Delete last train.\n\
|
|
train list : Print list of saved trains.\n\
|
|
train [nn] stop : Stop nth train.\n\
|
|
train [nn] speed [sp] : Set nth train speed.\n\
|
|
train [nn] function [x] : Toggle x'ed fuction on train n.\n\
|
|
train [nn] reverse : Reverse train n.\n\
|
|
stop : stop all trains\n\
|
|
power off : power off the rail\n\
|
|
power on : power on the rail\n\
|
|
power auto : power off the rail when no trains are moveing\n\
|
|
dump : prints epprom contence\n\
|
|
erase : Erase epprom.\n"));
|
|
}
|
|
|
|
|
|
void trainDispatch(char* inBuffer, Serial* serial)
|
|
{
|
|
if(powerIsOn == false)
|
|
{
|
|
powerIsOn = true;
|
|
Train::setOutput(Train::LOW);
|
|
_delay_ms(100);
|
|
}
|
|
if( strcmp(inBuffer, "add") == 0 )
|
|
{
|
|
char* token = strtok(NULL, " ");
|
|
uint8_t address = strtol(token, nullptr, 2 );
|
|
if(address != 0 && storedTrains < MAX_TRAINS)
|
|
{
|
|
trains[storedTrains].setAddress(address);
|
|
|
|
uint8_t size = snprintf(buffer, SNPRINTF_BUFFER_SIZE, "TRAIN saved! NUMBER: %u ADRESS: %s%s\n", storedTrains, bit_rep[address >> 4], bit_rep[address & 0x0F]);
|
|
serial->write(buffer, size);
|
|
|
|
storedTrains++;
|
|
save_state();
|
|
}
|
|
else serial->write_p(PSTR("Usage: train add [address]"));
|
|
}
|
|
else if( strcmp(inBuffer, "delete") == 0)
|
|
{
|
|
serial->write_p(PSTR("Train: "));
|
|
serial->write(storedTrains);
|
|
serial->write_p(PSTR(" deleted\n"));
|
|
storedTrains--;
|
|
}
|
|
else if( strcmp(inBuffer, "list") == 0 )
|
|
{
|
|
serial->write_p(PSTR("Trains:\n"));
|
|
for(uint8_t i = 0; i < storedTrains; i++)
|
|
{
|
|
snprintf(buffer, SNPRINTF_BUFFER_SIZE, "NUMBER: %u ID: %s%s CURRENT SPD: %u\n", i, bit_rep[trains[i].getAddress() >> 4], bit_rep[trains[i].getAddress() & 0x0F], trains[i].getSpeed());
|
|
serial->write(buffer, SNPRINTF_BUFFER_SIZE);
|
|
}
|
|
serial->putChar('\n');
|
|
}
|
|
else\
|
|
{
|
|
uint8_t id = strtol(inBuffer, nullptr, 10);
|
|
if(id < storedTrains )
|
|
{
|
|
char* token = strtok(NULL, " ");
|
|
if( strcmp(token, "speed") == 0 )
|
|
{
|
|
token = strtok(NULL, " ");
|
|
trains[id].setSpeed(atoi(token));
|
|
}
|
|
else if( strcmp(token, "function") == 0 )
|
|
{
|
|
token = strtok(NULL, " ");
|
|
trains[id].sendFunction(atoi(token));
|
|
}
|
|
else if( strcmp(token, "reverse") == 0 ) trains[id].reverse();
|
|
else if( strcmp(token, "stop") )trains[id].setSpeed(0);
|
|
else serial->write_p(PSTR("Not a valid command\n"));
|
|
}
|
|
else serial->write_p(PSTR("Id out of range.\n"));
|
|
}
|
|
}
|
|
|
|
void powerDispatch(char* token, Serial* serial)
|
|
{
|
|
if(strcmp(token, "off") == 0)
|
|
{
|
|
for(uint16_t i = 0; i < storedTrains; i++)
|
|
{
|
|
trains[i].setSpeed(0);
|
|
}
|
|
Train::setOutput(Train::OFF);
|
|
powerIsOn = false;
|
|
}
|
|
else if( strcmp(token, "on") == 0)
|
|
{
|
|
for(uint16_t i = 0; i < storedTrains; i++)
|
|
{
|
|
trains[i].setSpeed(0);
|
|
}
|
|
Train::setOutput(Train::LOW);
|
|
}
|
|
else if(strcmp(token, "auto") == 0)
|
|
{
|
|
token = strtok(NULL, " ");
|
|
if(strcmp(token, "on") == 0)
|
|
{
|
|
autoff = true;
|
|
serial->write_p(PSTR("auto power off turned on.\n"));
|
|
save_state();
|
|
}
|
|
else if(strcmp(token, "off") == 0)
|
|
{
|
|
autoff = false;
|
|
serial->write_p(PSTR("auto power off turned off.\n"));
|
|
save_state();
|
|
}
|
|
else
|
|
{
|
|
serial->write_p(PSTR("argument must be \"on\" or \"off\". This feature is currently "));
|
|
autoff ? serial->write_p(PSTR("on.\n")) : serial->write_p(PSTR("off.\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void serialDispatch(Serial* serial)
|
|
{
|
|
if(serial->dataIsWaiting())
|
|
{
|
|
char buffer[COMMAND_BUFFER_SIZE];
|
|
unsigned int length = serial->getString(buffer, COMMAND_BUFFER_SIZE);
|
|
if(length > 2)
|
|
{
|
|
serial->write_p(PSTR("Got: "));
|
|
serial->putChar('\"');
|
|
serial->write(buffer, length);
|
|
serial->write("\"\n");
|
|
char* token = strtok(buffer, " ");
|
|
if(length > 4 && strcmp(token, "train") == 0)
|
|
{
|
|
token = strtok(NULL, " ");
|
|
trainDispatch(token, serial);
|
|
}
|
|
else if(length > 4 && strncmp(token, "erase", 4) == 0)
|
|
{
|
|
for(uint16_t i = 0; i < MAX_TRAINS+1; i++) EEPROM_write_char(i, 0);
|
|
serial->write_p(PSTR("EEPROM erased\n"));
|
|
storedTrains = 0;
|
|
}
|
|
else if(length > 3 && strcmp(token, "dump") == 0)
|
|
{
|
|
for(uint16_t i = 0; i < MAX_TRAINS+32; i++)
|
|
{
|
|
if(i != 0) serial->putChar(',');
|
|
serial->write((uint16_t)EEPROM_read_char(i));
|
|
}
|
|
serial->putChar('\n');
|
|
}
|
|
else if(length > 3 && strcmp(token, "stop") == 0)
|
|
{
|
|
for(uint16_t i = 0; i < storedTrains; i++)
|
|
{
|
|
trains[i].setSpeed(0);
|
|
}
|
|
}
|
|
else if(length > 3 && strcmp(token, "power") == 0)
|
|
{
|
|
token = strtok(NULL, " ");
|
|
powerDispatch(token, serial);
|
|
}
|
|
else if(length > 3 && strcmp(token, "help") == 0)
|
|
{
|
|
printHelp(serial);
|
|
}
|
|
else
|
|
{
|
|
serial->putChar('\"');
|
|
serial->write(buffer, length);
|
|
serial->putChar('\"');
|
|
serial->write_p(PSTR(" is not a valid command\n"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
TCNT0 = 0;
|
|
TIMSK0 = 0b00000001; //turn on timer0 interupt on OCIEB
|
|
TCCR0B = (1<<CS02) /*| (1<<CS00)*/; // run timer0 with /1024 scaler
|
|
|
|
DDRD = (1 << PD2) | (1 << PD3);
|
|
|
|
restore_state();
|
|
|
|
autoff ? Train::setOutput(Train::OFF) : Train::setOutput(Train::LOW);
|
|
|
|
sei();
|
|
|
|
Serial serial;
|
|
|
|
serial.write_p(PSTR("TrainController v0.1 starting\n"));
|
|
while(true)
|
|
{
|
|
serialDispatch(&serial);
|
|
}
|
|
return 0;
|
|
}
|