]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/intellibox.cpp
Fix uninitialized variables in Intellibox driver
[r2c2.git] / source / libr2c2 / intellibox.cpp
1 #include <fcntl.h>
2 #include <termios.h>
3 #include <sys/poll.h>
4 #include <msp/io/print.h>
5 #include <msp/time/units.h>
6 #include <msp/time/utils.h>
7 #include "intellibox.h"
8 #include "tracktype.h"
9 #include "vehicletype.h"
10
11 using namespace std;
12 using namespace Msp;
13
14 namespace R2C2 {
15
16 Intellibox::Intellibox(const string &dev):
17         serial(dev),
18         power(false),
19         halted(false),
20         update_sensors(false),
21         command_sent(false)
22 {
23         static unsigned baud[]= { 2400, 4800, 9600, 19200, 0 };
24
25         serial.set_stop_bits(2);
26
27         bool ok = false;
28         bool p50 = false;
29         for(unsigned i=0; baud[i]; ++i)
30         {
31                 serial.set_baud_rate(baud[i]);
32                 serial.put('\xC4');
33
34                 if(IO::poll(serial, IO::P_INPUT, 500*Time::msec))
35                 {
36                         IO::print("IB detected at %d bits/s\n", baud[i]);
37                         char buf[2];
38                         p50 = (serial.read(buf, 2)==2);
39                         ok = true;
40                         break;
41                 }
42         }
43
44         if(!ok)
45                 throw runtime_error("IB not detected");
46
47         if(p50)
48                 serial.write("xZzA1\r", 6);
49
50         command(CMD_STATUS);
51 }
52
53 void Intellibox::set_power(bool p)
54 {
55         power = p;
56         if(power)
57                 command(CMD_POWER_ON);
58         else
59                 command(CMD_POWER_OFF);
60         signal_power.emit(power);
61 }
62
63 void Intellibox::halt(bool h)
64 {
65         halted = h;
66         if(halted)
67         {
68                 for(map<unsigned, Locomotive>::iterator i=locos.begin(); i!=locos.end(); ++i)
69                         if(i->second.speed)
70                                 set_loco_speed(i->first, 0);
71         }
72
73         signal_halt.emit(halted);
74 }
75
76 const char *Intellibox::enumerate_protocols(unsigned i) const
77 {
78         ++i;
79         if(i==MM)
80                 return "MM";
81         else if(i==MM_27)
82                 return "MM-27";
83         return 0;
84 }
85
86 unsigned Intellibox::get_protocol_speed_steps(const string &proto_name) const
87 {
88         Protocol proto = map_protocol(proto_name);
89         if(proto==MM)
90                 return 14;
91         else if(proto==MM_27)
92                 return 27;
93         return 0;
94 }
95
96 void Intellibox::add_loco(unsigned addr, const string &proto_name, const VehicleType &type)
97 {
98         Protocol proto = map_protocol(proto_name);
99
100         if(!locos.count(addr))
101         {
102                 Locomotive &loco = locos[addr];
103                 loco.protocol = proto;
104                 if(type.get_max_function()>4)
105                 {
106                         loco.ext_func = true;
107                         locos[addr+1].protocol = NONE;
108                 }
109
110                 unsigned char data[2];
111                 data[0] = addr&0xFF;
112                 data[1] = (addr>>8)&0xFF;
113                 command(CMD_LOK_STATUS, addr, data, 2);
114         }
115 }
116
117 void Intellibox::set_loco_speed(unsigned addr, unsigned speed)
118 {
119         Locomotive &loco = locos[addr];
120         if(loco.protocol==NONE)
121                 return;
122
123         if(speed==loco.speed)
124         {
125                 if(loco.pending_half_step)
126                 {
127                         loco.pending_half_step = 0;
128                         loco.half_step_delay = Time::TimeStamp();
129                         signal_loco_speed.emit(addr, speed, loco.reverse);
130                 }
131                 return;
132         }
133         if(speed && halted)
134                 return;
135
136         if(loco.protocol==MM_27)
137         {
138                 if(speed>27)
139                         speed = 27;
140
141                 if(speed>loco.speed && !(speed&1))
142                 {
143                         loco.pending_half_step = -1;
144                         speed |= 1;
145                 }
146                 else if(speed<loco.speed && (speed&1))
147                 {
148                         loco.pending_half_step = 1;
149                         speed &= ~1;
150                 }
151                 else
152                         loco.pending_half_step = 0;
153                 loco.half_step_delay = Time::TimeStamp();
154
155                 loco_command(addr, (speed+1)/2, loco.reverse, loco.funcs, false);
156         }
157         else if(loco.protocol==MM)
158         {
159                 if(speed>14)
160                         speed = 14;
161
162                 loco_command(addr, speed, loco.reverse, loco.funcs, false);
163         }
164         loco.speed = speed;
165 }
166
167 void Intellibox::set_loco_reverse(unsigned addr, bool rev)
168 {
169         Locomotive &loco = locos[addr];
170         if(loco.protocol==NONE || rev==loco.reverse)
171                 return;
172
173         loco.speed = 0;
174         loco.reverse = rev;
175         loco_command(addr, 0, rev, loco.funcs, false);
176 }
177
178 void Intellibox::set_loco_function(unsigned addr, unsigned func, bool state)
179 {
180         Locomotive &loco = locos[addr];
181         if(loco.protocol==NONE)
182                 return;
183
184         if(state)
185                 loco.funcs |= 1<<func;
186         else
187                 loco.funcs &= ~(1<<func);
188         if(func<=4)
189                 loco_command(addr, loco.speed, loco.reverse, loco.funcs, true);
190         else if(loco.ext_func && func<=8)
191                 loco_command(addr+1, 0, false, (loco.funcs>>4)&0x1E, true);
192         signal_loco_function.emit(addr, func, state);
193 }
194
195 void Intellibox::add_turnout(unsigned addr, const TrackType &type)
196 {
197         add_turnout(addr, type.get_state_bits(), false);
198 }
199
200 void Intellibox::add_turnout(unsigned addr, unsigned bits, bool signal)
201 {
202         if(!turnouts.count(addr))
203         {
204                 Turnout &turnout = turnouts[addr];
205                 turnout.bits = bits;
206                 turnout.signal = signal;
207
208                 unsigned char data[2];
209                 data[0] = addr&0xFF;
210                 data[1] = (addr>>8)&0xFF;
211                 command(CMD_TURNOUT_STATUS, addr, data, 2);
212                 for(unsigned i=1; i<turnout.bits; ++i)
213                 {
214                         turnouts[addr+i].bits = 0;
215
216                         ++data[0];
217                         if(!data[0])
218                                 ++data[1];
219                         command(CMD_TURNOUT_STATUS, addr+i, data, 2);
220                 }
221         }
222 }
223
224 void Intellibox::turnout_state_changed(unsigned addr, const Turnout &turnout) const
225 {
226         if(turnout.signal)
227                 signal_signal.emit(addr, turnout.state);
228         else
229                 signal_turnout.emit(addr, turnout.state);
230 }
231
232 void Intellibox::set_turnout(unsigned addr, unsigned state)
233 {
234         Turnout &turnout = turnouts[addr];
235         unsigned mask = (1<<turnout.bits)-1;
236         if(((state^turnout.state)&mask)==0 || ((state^turnout.pending)&mask)==0 || !turnout.synced)
237         {
238                 turnout.state = state;
239                 turnout.pending = state;
240                 turnout_state_changed(addr, turnout);
241                 return;
242         }
243
244         turnout.state = (turnout.state&mask) | (state&~mask);
245         turnout.pending = state;
246         turnout.active = true;
247         turnout.off_timeout = Time::TimeStamp();
248
249         for(unsigned i=0; i<turnout.bits; ++i)
250                 if((state^turnout.state)&(1<<i))
251                         turnout_command(addr+i, !(state&(1<<i)), true);
252 }
253
254 unsigned Intellibox::get_turnout(unsigned addr) const
255 {
256         map<unsigned, Turnout>::const_iterator i = turnouts.find(addr);
257         if(i!=turnouts.end())
258                 return i->second.state;
259         return 0;
260 }
261
262 void Intellibox::add_signal(unsigned addr, const SignalType &)
263 {
264         add_turnout(addr, 1, true);
265 }
266
267 void Intellibox::set_signal(unsigned addr, unsigned state)
268 {
269         set_turnout(addr, state);
270 }
271
272 unsigned Intellibox::get_signal(unsigned addr) const
273 {
274         return get_turnout(addr);
275 }
276
277 void Intellibox::add_sensor(unsigned addr)
278 {
279         if(!sensors.count(addr))
280         {
281                 sensors[addr];
282                 update_sensors = true;
283         }
284 }
285
286 bool Intellibox::get_sensor(unsigned addr) const
287 {
288         map<unsigned, Sensor>::const_iterator i = sensors.find(addr);
289         if(i!=sensors.end())
290                 return i->second.state;
291         return false;
292 }
293
294 void Intellibox::tick()
295 {
296         const Time::TimeStamp t = Time::now();
297
298         if(t>next_event_query)
299         {
300                 next_event_query = t+200*Time::msec;
301                 command(CMD_EVENT);
302         }
303
304         for(map<unsigned, Locomotive>::iterator i=locos.begin(); i!=locos.end(); ++i)
305                 if(i->second.protocol==MM_27 && i->second.pending_half_step && i->second.half_step_delay && t>i->second.half_step_delay)
306                 {
307                         i->second.speed += i->second.pending_half_step;
308                         i->second.pending_half_step = 0;
309                         i->second.half_step_delay = Time::TimeStamp();
310                         loco_command(i->first, (i->second.speed+1)/2, i->second.reverse, i->second.funcs, false);
311                 }
312
313         for(map<unsigned, Turnout>::iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
314                 if(i->second.active && i->second.off_timeout && t>i->second.off_timeout)
315                 {
316                         i->second.active = false;
317                         i->second.off_timeout = Time::TimeStamp();
318                         for(unsigned j=0; j<i->second.bits; ++j)
319                                 turnout_command(i->first+j, !(i->second.state&(1<<j)), false);
320                 }
321
322         for(map<unsigned, Sensor>::iterator i=sensors.begin(); i!=sensors.end(); ++i)
323                 if(i->second.off_timeout && t>i->second.off_timeout)
324                 {
325                         i->second.state = false;
326                         i->second.off_timeout = Time::TimeStamp();
327                         signal_sensor.emit(i->first, false);
328                 }
329
330         if(update_sensors)
331         {
332                 unsigned max_addr = (--sensors.end())->first;
333                 unsigned char data[2];
334                 data[0] = 0;
335                 data[1] = (max_addr+7)/8;
336                 command(CMD_SENSOR_PARAM_SET, data, 2);
337                 command(CMD_SENSOR_REPORT);
338                 update_sensors = false;
339         }
340
341         if(!queue.empty() && command_sent)
342         {
343                 if(IO::poll(serial, IO::P_INPUT, Time::zero))
344                 {
345                         process_reply(t);
346                         queue.erase(queue.begin());
347                         command_sent = false;
348                 }
349                 else
350                         return;
351         }
352
353         if(!queue.empty())
354         {
355                 const CommandSlot &slot = queue.front();
356                 serial.write(reinterpret_cast<const char *>(slot.data), slot.length);
357                 command_sent = true;
358         }
359 }
360
361 void Intellibox::flush()
362 {
363         for(list<CommandSlot>::iterator i=queue.begin(); i!=queue.end(); ++i)
364         {
365                 serial.write(reinterpret_cast<const char *>(i->data), i->length);
366                 bool first = true;
367                 while(first ? IO::poll(serial, IO::P_INPUT) : IO::poll(serial, IO::P_INPUT, Time::zero))
368                 {
369                         char data[16];
370                         serial.read(data, 16);
371                         first = false;
372                 }
373         }
374
375         queue.clear();
376         command_sent = false;
377 }
378
379 Intellibox::Protocol Intellibox::map_protocol(const string &name) const
380 {
381         if(name=="MM")
382                 return MM;
383         else if(name=="MM-27")
384                 return MM_27;
385         else
386                 throw invalid_argument("Intellibox::map_protocol");
387 }
388
389 void Intellibox::command(Command cmd)
390 {
391         command(cmd, 0, 0);
392 }
393
394 void Intellibox::command(Command cmd, const unsigned char *data, unsigned len)
395 {
396         command(cmd, 0, data, len);
397 }
398
399 void Intellibox::command(Command cmd, unsigned addr, const unsigned char *data, unsigned len)
400 {
401         CommandSlot slot;
402         slot.cmd = cmd;
403         slot.addr = addr;
404         slot.data[0] = cmd;
405         copy(data, data+len, slot.data+1);
406         slot.length = 1+len;
407         queue.push_back(slot);
408 }
409
410 void Intellibox::loco_command(unsigned addr, unsigned speed, bool rev, unsigned funcs, bool setf)
411 {
412         unsigned char data[4];
413         data[0] = addr&0xFF;
414         data[1] = (addr>>8)&0xFF;
415
416         if(speed==0)
417                 data[2] = 0;
418         else if(speed==1)
419                 data[2] = 2;
420         else
421                 data[2] = (speed*19-18)/2;
422         
423         data[3] = (rev ? 0 : 0x20) | ((funcs&1) ? 0x10 : 0);
424
425         if(setf)
426                 data[3] |= 0x80 | ((funcs>>1)&0xF);
427
428         command(CMD_LOK, addr, data, 4);
429 }
430
431 void Intellibox::turnout_command(unsigned addr, bool state, bool active)
432 {
433         unsigned char data[2];
434         data[0] = addr&0xFF;
435         data[1] = ((addr>>8)&0x7) | (active ? 0x40 : 0) | (state ? 0x80 : 0);
436         command(CMD_TURNOUT, addr, data, 2);
437 }
438
439 void Intellibox::process_reply(const Time::TimeStamp &t)
440 {
441         Command cmd = queue.front().cmd;
442
443         if(cmd==CMD_STATUS)
444         {
445                 unsigned char status;
446                 read_all(&status, 1);
447                 power = status&0x08;
448                 signal_power.emit(power);
449         }
450         else if(cmd==CMD_EVENT)
451         {
452                 for(unsigned i=0;; ++i)
453                 {
454                         unsigned char byte;
455                         read_all(&byte, 1);
456
457                         if(i==0)
458                         {
459                                 if(byte&0x01)
460                                         command(CMD_EVENT_LOK);
461                                 if(byte&0x20)
462                                         command(CMD_EVENT_TURNOUT);
463                                 if(byte&0x04)
464                                         command(CMD_EVENT_SENSOR);
465                         }
466                         else if(i==1)
467                         {
468                                 if(byte&0x40)
469                                         command(CMD_STATUS);
470                         }
471
472                         if(!(byte&0x80))
473                                 break;
474                 }
475         }
476         else if(cmd==CMD_EVENT_LOK)
477         {
478                 while(1)
479                 {
480                         unsigned char data[5];
481                         read_all(data, 1);
482                         if(data[0]==0x80)
483                                 break;
484                         read_all(data+1, 4);
485                 }
486         }
487         else if(cmd==CMD_EVENT_TURNOUT)
488         {
489                 unsigned char count;
490                 read_all(&count, 1);
491                 for(unsigned i=0; i<count; ++i)
492                 {
493                         unsigned char data[2];
494                         read_all(data, 2);
495
496                         unsigned addr = data[0]+((data[1]&7)<<8);
497                         unsigned mask = 1;
498                         for(; !turnouts[addr].bits; --addr, mask<<=1) ;
499                         Turnout &turnout = turnouts[addr];
500
501                         unsigned bit = !(data[1]&0x80);
502                         turnout.state = (turnout.state&~mask) | (bit*mask);
503                         turnout.pending = turnout.state;
504                         turnout_state_changed(addr,turnout);
505                 }
506         }
507         else if(cmd==CMD_EVENT_SENSOR)
508         {
509                 while(1)
510                 {
511                         unsigned char mod;
512                         read_all(&mod, 1);
513                         if(!mod)
514                                 break;
515
516                         unsigned char data[2];
517                         read_all(data, 2);
518                         for(unsigned i=0; i<16; ++i)
519                         {
520                                 unsigned addr = mod*16+i-15;
521                                 bool state = (data[i/8]>>(7-i%8))&1;
522
523                                 Sensor &sensor = sensors[addr];
524                                 if(state)
525                                 {
526                                         sensor.off_timeout = Time::TimeStamp();
527                                         if(!sensor.state)
528                                         {
529                                                 sensor.state = state;
530                                                 signal_sensor(addr, state);
531                                         }
532                                 }
533                                 else if(sensor.state)
534                                         sensor.off_timeout = t+700*Time::msec;
535                         }
536                 }
537         }
538         else if(cmd==CMD_LOK)
539         {
540                 Error err;
541                 read_status(&err);
542
543                 if(err==ERR_NO_ERROR)
544                 {
545                         unsigned addr = queue.front().addr;
546                         Locomotive &loco = locos[addr];
547                         if(loco.protocol)
548                         {
549                                 signal_loco_speed.emit(addr, loco.speed+loco.pending_half_step, loco.reverse);
550                                 if(loco.pending_half_step)
551                                         loco.half_step_delay = Time::now()+500*Time::msec;
552                         }
553                 }
554                 else
555                         error(cmd, err);
556         }
557         else if(cmd==CMD_TURNOUT)
558         {
559                 Error err;
560                 read_status(&err);
561
562                 unsigned addr = queue.front().addr;
563                 unsigned mask = 1;
564                 for(; !turnouts[addr].bits; --addr, mask<<=1) ;
565                 Turnout &turnout = turnouts[addr];
566
567                 if(err==ERR_NO_ERROR)
568                 {
569                         turnout.state = (turnout.state&~mask) | (turnout.pending&mask);
570                         if(turnout.active)
571                         {
572                                 if(turnout.state==turnout.pending)
573                                         turnout_state_changed(addr, turnout);
574                                 turnout.off_timeout = t+500*Time::msec;
575                         }
576                 }
577                 else if(err==ERR_NO_I2C_SPACE)
578                         queue.push_back(queue.front());
579                 else
580                 {
581                         turnout.pending = (turnout.pending&~mask) | (turnout.state&mask);
582                         error(cmd, err);
583                 }
584         }
585         else if(cmd==CMD_TURNOUT_STATUS)
586         {
587                 Error err;
588                 read_status(&err);
589
590                 if(err==ERR_NO_ERROR)
591                 {
592                         unsigned char data;
593                         read_all(&data, 1);
594
595                         unsigned addr = queue.front().addr;
596                         unsigned mask = 1;
597                         for(; !turnouts[addr].bits; --addr, mask<<=1) ;
598                         Turnout &turnout = turnouts[addr];
599
600                         bool bit = !(data&0x04);
601                         if(bit!=((turnout.state&mask)!=0))
602                         {
603                                 turnout.state = (turnout.state&~mask) | (bit*mask);
604                                 turnout.pending = turnout.state;
605                                 turnout_state_changed(addr, turnout);
606                         }
607
608                         turnout.synced = true;
609                 }
610                 else
611                         error(cmd, err);
612         }
613         else if(cmd==CMD_LOK_STATUS)
614         {
615                 Error err;
616                 read_status(&err);
617
618                 if(err==ERR_NO_ERROR)
619                 {
620                         unsigned char data[3];
621                         read_all(data, 3);
622
623                         unsigned addr = queue.front().addr;
624                         Locomotive &loco = locos[addr];
625
626                         unsigned speed = (data[0]<=1 ? 0 : data[0]*2/19+1);
627                         bool reverse = !(data[1]&0x20);
628                         bool speed_changed = (speed!=loco.speed || reverse!=loco.reverse);
629
630                         loco.speed = speed;
631                         loco.reverse = reverse;
632
633                         unsigned funcs = (data[1]&0xF)<<1;
634                         if(data[1]&0x10)
635                                 funcs |= 1;
636                         unsigned funcs_changed = loco.funcs^funcs;
637                         loco.funcs = funcs;
638
639                         if(speed_changed)
640                                 signal_loco_speed.emit(addr, loco.speed, loco.reverse);
641                         for(unsigned i=0; i<5; ++i)
642                                 if(funcs_changed&(1<<i))
643                                         signal_loco_function.emit(addr, i, loco.funcs&(1<<i));
644                 }
645                 else
646                         error(cmd, err);
647         }
648         else
649         {
650                 unsigned expected_bytes = 0;
651                 if(cmd==CMD_FUNC_STATUS)
652                         expected_bytes = 1;
653                 if(cmd==CMD_TURNOUT_GROUP_STATUS)
654                         expected_bytes = 2;
655                 if(cmd==CMD_LOK_CONFIG)
656                         expected_bytes = 4;
657
658                 Error err;
659                 read_status(&err);
660
661                 if(err==ERR_NO_ERROR)
662                 {
663                         unsigned char data[8];
664                         read_all(data, expected_bytes);
665                 }
666                 else
667                         error(cmd, err);
668         }
669 }
670
671 unsigned Intellibox::read_all(unsigned char *buf, unsigned len)
672 {
673         unsigned pos = 0;
674         while(pos<len)
675                 pos += serial.read(reinterpret_cast<char *>(buf+pos), len-pos);
676
677         return pos;
678 }
679
680 unsigned Intellibox::read_status(Error *err)
681 {
682         unsigned char c;
683         unsigned ret = read_all(&c, 1);
684         *err = static_cast<Error>(c);
685         return ret;
686 }
687
688 void Intellibox::error(Command cmd, Error err)
689 {
690         const char *cmd_str = 0;
691         switch(cmd)
692         {
693         case CMD_LOK: cmd_str = "CMD_LOK"; break;
694         case CMD_LOK_STATUS: cmd_str = "CMD_LOK_STATUS"; break;
695         case CMD_LOK_CONFIG: cmd_str = "CMD_LOK_CONFIG"; break;
696         case CMD_FUNC: cmd_str = "CMD_FUNC"; break;
697         case CMD_FUNC_STATUS: cmd_str = "CMD_FUNC_STATUS"; break;
698         case CMD_TURNOUT: cmd_str = "CMD_TURNOUT"; break;
699         case CMD_TURNOUT_FREE: cmd_str = "CMD_TURNOUT_FREE"; break;
700         case CMD_TURNOUT_STATUS: cmd_str = "CMD_TURNOUT_STATUS"; break;
701         case CMD_TURNOUT_GROUP_STATUS: cmd_str = "CMD_TURNOUT_GROUP_STATUS"; break;
702         case CMD_SENSOR_STATUS: cmd_str = "CMD_SENSOR_STATUS"; break;
703         case CMD_SENSOR_REPORT: cmd_str = "CMD_SENSOR_REPORT"; break;
704         case CMD_SENSOR_PARAM_SET: cmd_str = "CMD_SENSOR_PARAM_SET"; break;
705         case CMD_STATUS: cmd_str = "CMD_STATUS"; break;
706         case CMD_POWER_OFF: cmd_str = "CMD_POWER_OFF"; break;
707         case CMD_POWER_ON: cmd_str = "CMD_POWER_ON"; break;
708         case CMD_NOP: cmd_str = "CMD_NOP"; break;
709         case CMD_EVENT: cmd_str = "CMD_EVENT"; break;
710         case CMD_EVENT_LOK: cmd_str = "CMD_EVENT_LOK"; break;
711         case CMD_EVENT_TURNOUT: cmd_str = "CMD_EVENT_TURNOUT"; break;
712         case CMD_EVENT_SENSOR: cmd_str = "CMD_EVENT_SENSOR"; break;
713         default: cmd_str = "(unknown command)";
714         }
715
716         const char *err_str = 0;
717         switch(err)
718         {
719         case ERR_NO_ERROR: err_str = "ERR_NO_ERROR"; break;
720         case ERR_SYS_ERROR: err_str = "ERR_SYS_ERROR"; break;
721         case ERR_BAD_PARAM: err_str = "ERR_BAD_PARAM"; break;
722         case ERR_POWER_OFF: err_str = "ERR_POWER_OFF"; break;
723         case ERR_NO_LOK_SPACE: err_str = "ERR_NO_LOK_SPACE"; break;
724         case ERR_NO_TURNOUT_SPACE: err_str = "ERR_NO_TURNOUT_SPACE"; break;
725         case ERR_NO_DATA: err_str = "ERR_NO_DATA"; break;
726         case ERR_NO_SLOT: err_str = "ERR_NO_SLOT"; break;
727         case ERR_BAD_LOK_ADDR: err_str = "ERR_BAD_LOK_ADDR"; break;
728         case ERR_LOK_BUSY: err_str = "ERR_LOK_BUSY"; break;
729         case ERR_BAD_TURNOUT_ADDR: err_str = "ERR_BAD_TURNOUT_ADDR"; break;
730         case ERR_BAD_SO_VALUE: err_str = "ERR_BAD_SO_VALUE"; break;
731         case ERR_NO_I2C_SPACE: err_str = "ERR_NO_I2C_SPACE"; break;
732         case ERR_LOW_TURNOUT_SPACE: err_str = "ERR_LOW_TURNOUT_SPACE"; break;
733         case ERR_LOK_HALTED: err_str = "ERR_LOK_HALTED"; break;
734         case ERR_LOK_POWER_OFF: err_str = "ERR_LOK_POWER_OFF"; break;
735         default: cmd_str = "(unknown error)";
736         }
737
738         IO::print("Error: %s: %s\n", cmd_str, err_str);
739 }
740
741
742 Intellibox::Locomotive::Locomotive():
743         protocol(NONE),
744         ext_func(false),
745         speed(0),
746         reverse(false),
747         funcs(0),
748         pending_half_step(0)
749 { }
750
751
752 Intellibox::Turnout::Turnout():
753         bits(1),
754         state(0),
755         active(false),
756         synced(false),
757         pending(0),
758         signal(false)
759 { }
760
761
762 Intellibox::Sensor::Sensor():
763         state(false)
764 { }
765
766 } // namespace R2C2