3 This file is part of the MSP Märklin suite
4 Copyright © 2010 Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
11 #include <msp/io/print.h>
12 #include <msp/time/units.h>
13 #include <msp/time/utils.h>
14 #include "intellibox.h"
21 Intellibox::Intellibox(const string &dev):
23 update_sensors(false),
26 serial_fd = ::open(dev.c_str(), O_RDWR);
28 throw Exception("Couldn't open serial port\n");
30 static unsigned baud[]=
40 tcgetattr(serial_fd, &attr);
42 attr.c_cflag |= CSTOPB;
46 for(unsigned i=0; baud[i]; i+=2)
48 cfsetospeed(&attr, baud[i+1]);
49 tcsetattr(serial_fd, TCSADRAIN, &attr);
51 write(serial_fd, "\xC4", 1);
53 pollfd pfd = { serial_fd, POLLIN, 0 };
54 if(poll(&pfd, 1, 500)>0)
56 IO::print("IB detected at %d bits/s\n", baud[i]);
58 p50 = (read(serial_fd, buf, 2)==2);
65 throw Exception("IB not detected");
68 write(serial_fd, "xZzA1\r", 6);
73 void Intellibox::set_power(bool p)
77 command(CMD_POWER_ON);
79 command(CMD_POWER_OFF);
80 signal_power.emit(power);
83 void Intellibox::add_loco(unsigned addr)
85 if(!locos.count(addr))
89 unsigned char data[2];
91 data[1] = (addr>>8)&0xFF;
92 command(CMD_LOK_STATUS, addr, data, 2);
96 void Intellibox::set_loco_speed(unsigned addr, unsigned speed)
98 Locomotive &loco = locos[addr];
100 loco_command(addr, speed, loco.reverse, loco.funcs|0x100);
101 signal_loco_speed.emit(addr, speed, loco.reverse);
104 void Intellibox::set_loco_reverse(unsigned addr, bool rev)
106 Locomotive &loco = locos[addr];
108 loco_command(addr, loco.speed, rev, loco.funcs|0x100);
109 signal_loco_speed.emit(addr, loco.speed, rev);
112 void Intellibox::set_loco_function(unsigned addr, unsigned func, bool state)
114 Locomotive &loco = locos[addr];
116 loco.funcs |= 1<<func;
118 loco.funcs &= ~(1<<func);
119 loco_command(addr, loco.speed, loco.reverse, loco.funcs);
120 signal_loco_function.emit(addr, func, state);
123 void Intellibox::add_turnout(unsigned addr)
125 if(!turnouts.count(addr))
129 unsigned char data[2];
131 data[1] = (addr>>8)&0xFF;
132 command(CMD_TURNOUT_STATUS, addr, data, 2);
136 void Intellibox::set_turnout(unsigned addr, bool state)
138 Turnout &turnout = turnouts[addr];
139 if(state==turnout.state || state==turnout.pending)
142 turnout.pending = state;
143 turnout.active = true;
145 turnout_command(addr, state, true);
148 bool Intellibox::get_turnout(unsigned addr) const
150 map<unsigned, Turnout>::const_iterator i = turnouts.find(addr);
151 if(i!=turnouts.end())
152 return i->second.state;
156 void Intellibox::add_sensor(unsigned addr)
158 if(!sensors.count(addr))
161 update_sensors = true;
165 bool Intellibox::get_sensor(unsigned addr) const
167 map<unsigned, Sensor>::const_iterator i = sensors.find(addr);
169 return i->second.state;
173 void Intellibox::tick()
175 const Time::TimeStamp t = Time::now();
177 if(t>next_event_query)
179 next_event_query = t+200*Time::msec;
183 for(map<unsigned, Turnout>::iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
184 if(i->second.active && i->second.off_timeout && t>i->second.off_timeout)
186 i->second.active = false;
187 i->second.off_timeout = Time::TimeStamp();
188 turnout_command(i->first, i->second.state, false);
191 for(map<unsigned, Sensor>::iterator i=sensors.begin(); i!=sensors.end(); ++i)
192 if(i->second.off_timeout && t>i->second.off_timeout)
194 i->second.state = false;
195 i->second.off_timeout = Time::TimeStamp();
196 signal_sensor.emit(i->first, false);
201 unsigned max_addr = (--sensors.end())->first;
202 unsigned char data[2];
204 data[1] = (max_addr+7)/8;
205 command(CMD_SENSOR_PARAM_SET, data, 2);
206 command(CMD_SENSOR_REPORT);
207 update_sensors = false;
210 if(!queue.empty() && command_sent)
212 pollfd pfd = { serial_fd, POLLIN, 0 };
213 if(poll(&pfd, 1, 0)>0)
216 queue.erase(queue.begin());
217 command_sent = false;
225 const CommandSlot &slot = queue.front();
226 write(serial_fd, slot.data, slot.length);
231 void Intellibox::flush()
233 for(list<CommandSlot>::iterator i=queue.begin(); i!=queue.end(); ++i)
234 write(serial_fd, i->data, i->length);
237 void Intellibox::command(Command cmd)
242 void Intellibox::command(Command cmd, const unsigned char *data, unsigned len)
244 command(cmd, 0, data, len);
247 void Intellibox::command(Command cmd, unsigned addr, const unsigned char *data, unsigned len)
253 copy(data, data+len, slot.data+1);
255 queue.push_back(slot);
258 void Intellibox::loco_command(unsigned addr, unsigned speed, bool rev, unsigned funcs)
260 unsigned char data[4];
262 data[1] = (addr>>8)&0xFF;
269 data[2] = (speed*19-18)/2;
271 data[3] = (rev ? 0 : 0x20) | ((funcs&1) ? 0x10 : 0);
274 data[3] |= 0x80 | ((funcs>>1)&0xF);
276 command(CMD_LOK, addr, data, 4);
279 void Intellibox::turnout_command(unsigned addr, bool state, bool active)
281 unsigned char data[2];
283 data[1] = ((addr>>8)&0x7) | (active ? 0x40 : 0) | (state ? 0x80 : 0);
284 command(CMD_TURNOUT, addr, data, 2);
287 void Intellibox::process_reply(const Time::TimeStamp &t)
289 Command cmd = queue.front().cmd;
293 unsigned char status;
294 read_all(&status, 1);
296 signal_power.emit(power);
298 else if(cmd==CMD_EVENT)
300 for(unsigned i=0;; ++i)
308 command(CMD_EVENT_LOK);
310 command(CMD_EVENT_TURNOUT);
312 command(CMD_EVENT_SENSOR);
324 else if(cmd==CMD_EVENT_LOK)
328 unsigned char data[5];
335 else if(cmd==CMD_EVENT_TURNOUT)
339 for(unsigned i=0; i<count; ++i)
341 unsigned char data[2];
344 unsigned addr = data[0]+((data[1]&7)<<8);
345 signal_turnout.emit(addr, (data[1]&0x80)!=0);
348 else if(cmd==CMD_EVENT_SENSOR)
357 unsigned char data[2];
359 for(unsigned i=0; i<16; ++i)
361 unsigned addr = mod*16+i-15;
362 bool state = (data[i/8]>>(7-i%8))&1;
364 Sensor &sensor = sensors[addr];
367 sensor.off_timeout = Time::TimeStamp();
370 sensor.state = state;
371 signal_sensor(addr, state);
374 else if(sensor.state)
375 sensor.off_timeout = t+700*Time::msec;
379 else if(cmd==CMD_TURNOUT)
384 if(err==ERR_NO_ERROR)
386 unsigned addr = queue.front().addr;
387 Turnout &turnout = turnouts[addr];
388 turnout.state = turnout.pending;
391 signal_turnout.emit(addr, turnout.state);
392 turnout.off_timeout = t+500*Time::msec;
395 else if(err==ERR_NO_I2C_SPACE)
396 queue.push_back(queue.front());
398 else if(cmd==CMD_TURNOUT_STATUS)
403 if(err==ERR_NO_ERROR)
408 unsigned addr = queue.front().addr;
409 bool state = data&0x04;
411 Turnout &turnout = turnouts[addr];
412 if(state!=turnout.state)
414 turnout.state = state;
415 signal_turnout.emit(addr, turnout.state);
419 else if(cmd==CMD_LOK_STATUS)
424 if(err==ERR_NO_ERROR)
426 unsigned char data[3];
429 unsigned addr = queue.front().addr;
430 Locomotive &loco = locos[addr];
432 unsigned speed = (data[0]<=1 ? 0 : data[0]*2/19+1);
433 bool reverse = !(data[1]&0x20);
434 if(speed!=loco.speed || reverse!=loco.reverse)
437 loco.reverse = reverse;
438 signal_loco_speed.emit(addr, loco.speed, loco.reverse);
441 unsigned funcs = (data[1]&0xF)<<1;
444 if(funcs!=loco.funcs)
446 unsigned changed = loco.funcs^funcs;
448 for(unsigned i=0; i<5; ++i)
450 signal_loco_function.emit(addr, i, loco.funcs&(1<<i));
456 unsigned expected_bytes = 0;
457 if(cmd==CMD_FUNC_STATUS)
459 if(cmd==CMD_TURNOUT_GROUP_STATUS)
461 if(cmd==CMD_LOK_CONFIG)
467 if(err==ERR_NO_ERROR)
469 unsigned char data[8];
470 read_all(data, expected_bytes);
475 unsigned Intellibox::read_all(unsigned char *buf, unsigned len)
479 pos += read(serial_fd, buf+pos, len-pos);
485 Intellibox::Locomotive::Locomotive():
492 Intellibox::Turnout::Turnout():
498 Intellibox::Sensor::Sensor():
502 } // namespace Marklin