library "marklinnet";
};
};
+
+ program "serial"
+ {
+ source "source/serial";
+ build_info
+ {
+ incpath "source";
+ library "marklinnet";
+ };
+ };
};
--- /dev/null
+# $Id$
+
+MCU = atmega328p
+CLOCK = 16000000
+CFLAGS = -Wall -Os -ffunction-sections -fdata-sections -mmcu=$(MCU) -DF_CPU=$(CLOCK)
+LDFLAGS = -Os -Wl,--gc-sections -mmcu=$(MCU)
+
+help:
+ @echo "Targets:"
+ @echo " %.hex: Build firmware from %.c"
+ @echo " upload-%: Upload firmware to AVR"
+
+%.hex: %.elf
+ avr-objcopy -O ihex $< $@
+
+%.elf: %.o
+ avr-gcc $(LDFLAGS) -o $@ $<
+
+%.o: %.c
+ avr-gcc -c $(CFLAGS) -o $@ $<
+
+upload-%: %.hex
+ avrdude -p$(MCU) -cstk500v1 -P/dev/ttyUSB0 -b57600 -D -Uflash:w:$<:i
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+
+#define BIT(n) (1<<(n))
+#define NOP() __asm__("nop");
+
+uint8_t digits[11] =
+{
+ 0x3F,
+ 0x06,
+ 0x5B,
+ 0x4F,
+ 0x66,
+ 0x6D,
+ 0x7D,
+ 0x07,
+ 0x7F,
+ 0x6F,
+ 0x40
+};
+
+uint16_t read_input(void);
+uint16_t read_input_filtered(void);
+void write_serial(uint8_t);
+void write_7seg(uint8_t);
+
+uint8_t speed = 0;
+uint16_t funcs = 0;
+volatile uint8_t ticks = 0;
+volatile uint8_t rx_buf[3];
+volatile uint8_t rx_fill = 0;
+
+ISR(TIMER1_COMPA_vect)
+{
+ if(ticks<255)
+ ++ticks;
+}
+
+ISR(USART_RX_vect)
+{
+ uint8_t c = UDR0;
+ if(rx_fill==0)
+ {
+ if(c=='A' || c=='S')
+ rx_buf[rx_fill++] = c;
+ }
+ else
+ {
+ rx_buf[rx_fill++] = c;
+ if(rx_buf[0]=='A' && rx_fill==3)
+ {
+ uint8_t n = (rx_buf[1]-'0')*10+(rx_buf[2]-'0');
+ write_7seg(n/10);
+ write_7seg(n%10);
+ rx_fill = 0;
+ }
+ else if(rx_buf[0]=='S' && rx_fill==3)
+ {
+ speed = (rx_buf[1]-'0')*10+(rx_buf[2]-'0');
+ rx_fill = 0;
+ }
+ }
+}
+
+int main()
+{
+ uint16_t state = 0;
+
+ DDRD = 0x02; // 00000010
+ PORTD = 0xFC; // 11111100
+ DDRB = 0x3F; // 00111111
+ PORTB = 0x0F; // 00001111
+
+ write_7seg(10);
+ write_7seg(10);
+
+ // 9600 baud, 8N1
+ UBRR0H = 0;
+ UBRR0L = 103;
+ UCSR0C = BIT(UCSZ00) | BIT(UCSZ01);
+ UCSR0B = BIT(RXEN0) | BIT(TXEN0) | BIT(RXCIE0);
+
+ TCCR1A = 0;
+ TCCR1B = BIT(WGM12) | BIT(CS12);
+ OCR1AH = 24;
+ OCR1AL = 106;
+ TIMSK1 = BIT(OCIE1A);
+
+ sei();
+
+ while(1)
+ {
+ uint16_t toggled;
+ uint8_t i;
+ uint16_t input;
+
+ input = read_input_filtered();
+ input ^= 0xFFFC;
+
+ toggled = state^input;
+ state = input;
+
+ if((toggled&3)==2 && (state&1))
+ {
+ uint8_t action = 0;
+ if(state&2)
+ {
+ if(speed<14)
+ {
+ ++speed;
+ action = 1;
+ }
+ }
+ else
+ {
+ if(speed>0)
+ {
+ --speed;
+ action = 1;
+ }
+ else if(ticks>10)
+ action = 2;
+ }
+
+ if(action==1)
+ {
+ write_serial('S');
+ write_serial('0'+speed/10);
+ write_serial('0'+speed%10);
+ }
+ else if(action==2)
+ write_serial('R');
+
+ ticks = 0;
+ }
+
+ for(i=0; i<14; ++i)
+ {
+ uint16_t bit = 4<<i;
+ if(toggled&~state&bit)
+ {
+ if(i==0)
+ write_serial('N');
+ else if(i==1)
+ write_serial('P');
+ else
+ {
+ uint8_t f = i-2;
+ uint16_t fbit = 1<<f;
+ funcs ^= fbit;
+ write_serial((funcs&fbit) ? 'H' : 'L');
+ write_serial('0'+f);
+ }
+ }
+ }
+ }
+}
+
+uint16_t read_input(void)
+{
+ uint8_t row;
+ uint16_t input = 0;
+
+ for(row=0; row<4; ++row)
+ {
+ uint8_t pins;
+
+ PORTB = (PORTB|0x0F)&~(1<<row);
+ NOP();
+ NOP();
+ NOP();
+ NOP();
+ NOP();
+ NOP();
+ pins = PIND>>2;
+ input |= pins&3;
+ input |= (pins&0x3C)<<(row*4);
+ }
+
+ return input;
+}
+
+uint16_t read_input_filtered(void)
+{
+ uint16_t valid = 0xFFFF;
+ uint16_t prev;
+ uint16_t input;
+ uint8_t i;
+
+ prev = read_input();
+ for(i=0; i<20; ++i)
+ {
+ input = read_input();
+ valid &= ~(input^prev);
+ prev = input;
+ }
+
+ return input&valid;
+}
+
+void write_serial(uint8_t c)
+{
+ while(!(UCSR0A&(1<<UDRE0))) ;
+ UDR0 = c;
+}
+
+void write_7seg(uint8_t n)
+{
+ uint8_t segs = ~digits[n];
+ uint8_t i;
+ for(i=0; i<8; ++i)
+ {
+ PORTB &= ~0x20;
+ if(segs&0x80)
+ PORTB |= 0x10;
+ else
+ PORTB &= ~0x10;
+ PORTB |= 0x20;
+ segs <<= 1;
+ }
+}
comm = new Net::Communicator(*socket, proto, *this);
}
-NetTrain &Client::get_train(unsigned addr)
+NetTrain &Client::get_train(unsigned addr) const
{
- map<unsigned, NetTrain *>::iterator i = trains.find(addr);
+ map<unsigned, NetTrain *>::const_iterator i = trains.find(addr);
if(i==trains.end())
throw KeyError("Unknown train");
return *i->second;
get_train(pkt.address).process_packet(pkt);
}
+void Client::receive(const ErrorPacket &pkt)
+{
+ signal_error.emit(pkt.message);
+}
+
} // namespace Marklin
Msp::Net::PacketReceiver<TrainFunctionPacket>,
Msp::Net::PacketReceiver<TrainStatusPacket>,
Msp::Net::PacketReceiver<RouteInfoPacket>,
- Msp::Net::PacketReceiver<TrainRoutePacket>
+ Msp::Net::PacketReceiver<TrainRoutePacket>,
+ Msp::Net::PacketReceiver<ErrorPacket>
{
public:
sigc::signal<void, NetTrain &> signal_train_added;
+ sigc::signal<void, const std::string &> signal_error;
private:
const Catalogue &catalogue;
const Catalogue &get_catalogue() const { return catalogue; }
const std::list<std::string> &get_routes() const { return routes; }
- NetTrain &get_train(unsigned);
+ NetTrain &get_train(unsigned) const;
+ const std::map<unsigned, NetTrain *> &get_trains() const { return trains; }
private:
virtual void receive(const TrainInfoPacket &);
virtual void receive(const TrainStatusPacket &);
virtual void receive(const RouteInfoPacket &);
virtual void receive(const TrainRoutePacket &);
+ virtual void receive(const ErrorPacket &);
};
} // namespace Marklin
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/net/resolve.h>
+#include "serial.h"
+
+#include <msp/io/print.h>
+
+using namespace std;
+using namespace Msp;
+using namespace Marklin;
+
+Application::RegApp<Serial> Serial::reg;
+
+Serial::Serial(int, char **argv):
+ client(catalogue),
+ serial_port(argv[2]),
+ train(0),
+ rx_fill(0)
+{
+ DataFile::load(catalogue, "locos.dat");
+
+ client.use_event_dispatcher(event_disp);
+ client.signal_train_added.connect(sigc::mem_fun(this, &Serial::train_added));
+ client.signal_error.connect(sigc::mem_fun(this, &Serial::error));
+
+ string addr_str = argv[1];
+ if(addr_str.find(':')==string::npos)
+ addr_str += ":8315";
+ Net::SockAddr *addr = Net::resolve(addr_str);
+ client.connect(*addr);
+ delete addr;
+
+ serial_port.set_parameters("9600,8N1");
+ event_disp.add(serial_port);
+ serial_port.signal_data_available.connect(sigc::mem_fun(this, &Serial::data_available));
+}
+
+void Serial::tick()
+{
+ event_disp.tick();
+}
+
+void Serial::train_added(NetTrain &t)
+{
+ if(!train)
+ set_train(&t);
+}
+
+void Serial::error(const string &e)
+{
+ IO::print("%s\n", e);
+}
+
+void Serial::set_train(NetTrain *t)
+{
+ train = t;
+ serial_port.write(format("A%02d", train->get_address()));
+}
+
+void Serial::next_train()
+{
+ const map<unsigned, NetTrain *> &trains = client.get_trains();
+
+ map<unsigned, NetTrain *>::const_iterator i = trains.find(train->get_address());
+ ++i;
+ if(i==trains.end())
+ i = trains.begin();
+
+ set_train(i->second);
+}
+
+void Serial::prev_train()
+{
+ const map<unsigned, NetTrain *> &trains = client.get_trains();
+
+ map<unsigned, NetTrain *>::const_iterator i = trains.find(train->get_address());
+ if(i==trains.begin())
+ i = trains.end();
+ --i;
+
+ set_train(i->second);
+}
+
+void Serial::data_available()
+{
+ char c;
+ serial_port.read(&c, 1);
+ if(rx_fill==0)
+ {
+ if(c=='S' || c=='H' || c=='L')
+ rx_buf[rx_fill++] = c;
+ else if(c=='R')
+ {
+ IO::print("Reverse\n");
+ train->set_reverse(!train->get_reverse());
+ }
+ else if(c=='N')
+ next_train();
+ else if(c=='P')
+ prev_train();
+ }
+ else
+ {
+ rx_buf[rx_fill++] = c;
+ if(rx_buf[0]=='H' && rx_fill==2)
+ {
+ train->set_function(rx_buf[1]-'0', true);
+ IO::print("Func %d on\n", rx_buf[1]-'0');
+ rx_fill = 0;
+ }
+ else if(rx_buf[0]=='L' && rx_fill==2)
+ {
+ train->set_function(rx_buf[1]-'0', false);
+ IO::print("Func %d off\n", rx_buf[1]-'0');
+ rx_fill = 0;
+ }
+ else if(rx_buf[0]=='S' && rx_fill==3)
+ {
+ unsigned speed = (rx_buf[1]-'0')*10+(rx_buf[2]-'0');
+ IO::print("Set speed %d\n", speed);
+ train->set_speed(speed);
+ rx_fill = 0;
+ }
+ }
+}
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef SERIAL_H_
+#define SERIAL_H_
+
+#include <msp/core/application.h>
+#include <msp/io/serial.h>
+#include "network/client.h"
+
+class Serial: public Msp::Application
+{
+private:
+ Msp::IO::EventDispatcher event_disp;
+ Marklin::Catalogue catalogue;
+ Marklin::Client client;
+ Msp::IO::Serial serial_port;
+ Marklin::NetTrain *train;
+ char rx_buf[3];
+ unsigned rx_fill;
+
+ static Msp::Application::RegApp<Serial> reg;
+
+public:
+ Serial(int, char **);
+
+private:
+ virtual void tick();
+ void train_added(Marklin::NetTrain &);
+ void error(const std::string &);
+ void set_train(Marklin::NetTrain *);
+ void next_train();
+ void prev_train();
+ void data_available();
+};
+
+#endif