ebus: Support a temperature sensor for the shutter node.
[wk-misc.git] / ebus / shutter.c
1 /* shutter.c - Elektor Bus node to control a shutter
2  * Copyright (C) 2011 g10 Code GmbH
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, see <http://www.gnu.org/licenses/>.
16  */
17
18 /* This node is used to control the shutter in a living room.  The
19    shutter is operator by a motor with two coils and limit switches
20    connected to a solid state relays which is controlled by this node.
21    A future version of this controller will also support some sensor
22    to allow pulling down the shutter only to some percentage.  In any
23    case the limit switches of the motor control the endpoints.  The
24    control logic takes down the relays after the time required to pull
25    the shutters up or down plus some extra time.  The S2 and S3
26    switches are used for manually controlling the shutter.  They are
27    interlocked and operate in a toggle on/off way.  The hardware
28    itself is also interlocked so that it is not possible to drive both
29    motors at the same time.
30
31    The used relays are FINDER 34.51.7.012.0010 which feature a high
32    sensitive coil (~18mA) and are able to switch 6A (the datasheet
33    even exlicitly allows motors of up to 185W).  The first relay is
34    used for direction selection with NC connected to the pull-up motor
35    and NO to the pull-down motor.  Our control logic makes sure that
36    this relay is not switched under load.  The second one connects its
37    NO to the first relay and a snubber circuit is used to protect its
38    contacts.  They are both connected to the 12V rail and flyback
39    diodes are used for each.  Two BC547 are used to drive them.
40
41    Historical note: A first version of the shutter rig and this
42    software used two (not interlocked) solid state relays made up from
43    S202S02 opto-triacs along with snubbers and varistors to cope with
44    the voltage peaks induced by the other motor.  However the spikes
45    were obviously too high and after some weeks the triacs bricked
46    themself.  A second try using S202S01s had the same effect.  Better
47    save your money and use mechanical relays.
48  */
49
50 #include "hardware.h"
51
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <avr/io.h>
56 #include <avr/interrupt.h>
57 #include <avr/sleep.h>
58 #include <avr/eeprom.h>
59 #include <util/crc16.h>  /* For _crc_ibutton_update.  */
60
61 #include "ebus.h"
62 #include "proto-busctl.h"
63 #include "proto-h61.h"
64
65
66 #define MOTOR_on       (PORTC)  /* Switch motor on.  */
67 #define MOTOR_on_BIT   (PC2)
68 #define MOTOR_down     (PORTC)  /* Switch direction relay to down.  */
69 #define MOTOR_down_BIT (PC3)
70
71
72 #define SCHEDULE_ACTION_NOP   0 /* No operation.  */
73 #define SCHEDULE_ACTION_UP    1 /* Minute + 10s := pull up  */
74 #define SCHEDULE_ACTION_DOWN  5 /* Minute + 50s := pull down  */
75
76
77 /* Allowed action values.  Note that the hardware interlocks both
78    motors.  However to avoid switch the direction under load we first
79    set the direction, wait a moment and then switch the motor on.  */
80 enum __attribute__ ((packed)) action_values {
81   action_none = 0,
82   action_up,
83   action_down
84 };
85
86 enum __attribute__ ((packed)) motor_state_values {
87   motor_state_off = 0,
88   motor_state_pre_off,
89   motor_state_pre_off2,
90   motor_state_pre_up,
91   motor_state_pre_up2,
92   motor_state_up,
93   motor_state_up_ready,
94   motor_state_pre_down,
95   motor_state_pre_down2,
96   motor_state_down,
97   motor_state_down_ready
98 };
99
100 /* Remember the last action time.  */
101 static uint16_t schedule_last_tfound;
102
103 /* The next action to do.  It's value is changed by the commands
104    and a ticker is switching the motor depending on these values.  */
105 static volatile enum action_values next_action;
106
107
108 /* The shutter state byte.  This is directly used for the
109    QueryShutterState response message.  */
110 static volatile byte shutter_state;
111
112 /* Event flag, triggerd (surprise) every 10 seconds.  */
113 static volatile byte ten_seconds_event;
114
115 /* Sensor action delay counter and event flag.  */
116 static volatile uint16_t sensor_action_delay;
117 static volatile byte sensor_action_event;
118
119 /* A structure to control the sensor actions.  This is not used by an
120    ISR thus no need for volatile.  */
121 static struct
122 {
123   /* If set a sensor read out has been requested.  The value gives the
124      number of read out tries left.  If it is down to zero an error
125      message will be returned.  */
126   byte active;
127   /* The address to send the response to.  If a second client requests
128      a readout, we won't record the address but broadcast th
129      result.  */
130   byte addr_hi;
131   byte addr_lo;
132 } sensor_ctrl;
133
134
135 \f
136 /* Local prototypes.  */
137 static void init_eeprom (byte force);
138
139
140
141 \f
142 /* This code is called by the 1ms ticker interrupt service routine
143    with the current clock value given in milliseconds from 0..9999. */
144 void
145 ticker_bottom (unsigned int clock)
146 {
147   static volatile enum action_values current_action;
148   static volatile uint16_t action_delay;
149   static volatile enum motor_state_values state = motor_state_off;
150   enum action_values save_action;
151
152   /* Blink the activity LED if the motor is not in off state.  */
153   if (state != motor_state_off)
154     {
155       if (!(clock % 1000))
156         {
157           if ((LED_Collision & _BV(LED_Collision_BIT)))
158             LED_Collision &= ~_BV (LED_Collision_BIT);
159           else
160             LED_Collision |= _BV(LED_Collision_BIT);
161         }
162     }
163
164   /* Get next action.  */
165   save_action = current_action;
166   if (read_key_s2 ())
167     current_action = current_action != action_down ? action_down : action_none;
168   if (read_key_s3 ())
169     current_action = current_action != action_up? action_up : action_none;
170   if (current_action != save_action || next_action)
171     {
172       if (next_action)
173         {
174           current_action = next_action;
175           next_action = 0;
176         }
177       switch (current_action)
178         {
179         case action_none: state = motor_state_pre_off; break;
180         case action_up:   state = motor_state_pre_up; break;
181         case action_down: state = motor_state_pre_down; break;
182         }
183       /* When a new action has been selected we need to limit the
184          action delay to the minimum value so that we don't stick in
185          the long down or up states.  We also need to make sure that
186          there is an action delay so that we enter the state
187          transition switch. */
188       if (!action_delay)
189         action_delay = 1;
190       else if (action_delay > 200)
191         action_delay = 200;
192     }
193
194   if (action_delay && !--action_delay)
195     {
196       switch (state)
197         {
198         case motor_state_off:
199           LED_Collision &= ~_BV (LED_Collision_BIT); /* Clear LED.  */
200           /* Make sure the motor flags are cleared in the state info.  */
201           shutter_state &= 0b00111111;
202           break;
203
204         label_pre_off:
205         case motor_state_pre_off:
206           MOTOR_on &= ~_BV(MOTOR_on_BIT);    /* Switch motor off. */
207           action_delay = 200; /*ms*/
208           state = motor_state_pre_off2;
209           break;
210         case motor_state_pre_off2:
211           MOTOR_down &= ~_BV(MOTOR_down_BIT);/* Switch direction relay off. */
212           action_delay = 200; /*ms*/
213           state = motor_state_off;
214           break;
215
216         case motor_state_pre_up:
217           MOTOR_on &= ~_BV(MOTOR_on_BIT);    /* Switch motor off. */
218           action_delay = 200; /*ms*/
219           state = motor_state_pre_up2;
220           break;
221         case motor_state_pre_up2:
222           MOTOR_down &= ~_BV(MOTOR_down_BIT);/* Switch direction relay off. */
223           action_delay = 200; /*ms*/
224           state = motor_state_up;
225           break;
226         case motor_state_up:
227           MOTOR_on |= _BV(MOTOR_on_BIT);     /* Switch motor on. */
228           shutter_state = 0b11000000;
229           /*                |!------- Direction set to up
230            *                +-------- Motor running.        */
231           action_delay = 25000; /*ms*/
232           state = motor_state_up_ready;
233           break;
234         case motor_state_up_ready:
235           shutter_state = 0b00100000;
236           /*                  | !~~!- Value: 0 = 0% closed
237            *                  +------ State in bits 3..0 is valid.  */
238           goto label_pre_off;
239
240         case motor_state_pre_down:
241           MOTOR_on &= ~_BV(MOTOR_on_BIT);    /* Switch motor off. */
242           action_delay = 200; /*ms*/
243           state = motor_state_pre_down2;
244           break;
245         case motor_state_pre_down2:
246           MOTOR_down |= _BV(MOTOR_down_BIT); /* Switch direction relay on. */
247           action_delay = 200; /*ms*/
248           state = motor_state_down;
249           break;
250         case motor_state_down:
251           MOTOR_on |= _BV(MOTOR_on_BIT);     /* Switch motor on. */
252           shutter_state = 0b10000000;
253           /*                |!------- Direction set to down
254            *                +-------- Motor running.        */
255           action_delay = 25000; /*ms*/
256           state = motor_state_down_ready;
257           break;
258         case motor_state_down_ready:
259           shutter_state = 0b00101111;
260           /*                  | !~~!--- Value: 15 = 100% closed
261            *                  +-------- State in bits 3..0 is valid.  */
262           goto label_pre_off;
263         }
264     }
265
266   if (!clock)
267     {
268       ten_seconds_event = 1;
269       wakeup_main = 1;
270     }
271
272   if (sensor_action_delay)
273     {
274       if (!--sensor_action_delay)
275         {
276           sensor_action_event = 1;
277           wakeup_main = 1;
278         }
279     }
280 }
281
282
283 static void
284 send_dbgmsg (const char *string)
285 {
286   byte msg[16];
287
288   if (config.debug_flags)
289     {
290       msg[0] = PROTOCOL_EBUS_DBGMSG;
291       msg[1] = config.nodeid_hi;
292       msg[2] = config.nodeid_lo;
293       memset (msg+3, 0, 13);
294       strncpy (msg+3, string, 13);
295       csma_send_message (msg, 16);
296     }
297 }
298
299
300 /* static void */
301 /* send_dbgmsg_fmt (const char *format, ...) */
302 /* { */
303 /*   va_list arg_ptr; */
304 /*   char buffer[16]; */
305
306 /*   va_start (arg_ptr, format); */
307 /*   vsnprintf (buffer, 16, format, arg_ptr); */
308 /*   va_end (arg_ptr); */
309 /*   send_dbgmsg (buffer); */
310 /* } */
311
312
313 /* Process scheduled actions.  TIME is the current time.  If
314    FORCED_TLOW is not 0 the scheduler will run the last action between
315    FORCED_TLOW and TIME regardless on whether it has already been run.
316    This feature is required to cope with a changed system time:
317    Consider the shutter shall be closed at 19:00, the current system
318    time is 18:59 and the system time is updated to 19:10 - without
319    that feature the closing at 19:00 would get lost.  */
320 static void
321 process_schedule (uint16_t time, uint16_t forced_tlow)
322 {
323   uint16_t tlow, thigh, t, tfound;
324   byte i;
325
326   if (!time_has_been_set)
327     return; /* Don't schedule while running without a valid clock.  */
328
329   if (schedule_last_tfound > time || forced_tlow)
330     schedule_last_tfound = 0;  /* Time wrapped to the next week or forced
331                                   schedule action.  */
332
333   /* We look up to 5 minutes back into the past to cope with lost events.  */
334   time /= 6;
335   time *= 6;
336   tlow = forced_tlow? forced_tlow : time;
337   if (tlow >= 5 * 6)
338     tlow -= 5 * 6;
339   else
340     tlow = 0;
341   if (schedule_last_tfound && schedule_last_tfound > tlow)
342     tlow = schedule_last_tfound;
343   thigh = time + 5;
344
345   /* send_dbgmsg_fmt ("time=%u", time); */
346   /* send_dbgmsg_fmt ("lst=%u", schedule_last_tfound); */
347   /* send_dbgmsg_fmt ("low=%u", tlow); */
348   /* send_dbgmsg_fmt ("hig=%u", thigh); */
349
350   /* Walk the schedule and find the last entry. */
351   for (tfound=0, i=0; i < DIM (ee_data.u.shutterctl.schedule); i++)
352     {
353       t = eeprom_read_word (&ee_data.u.shutterctl.schedule[i]);
354       if (!t)
355         break;
356       if (t > tlow && t <= thigh)
357         tfound = t;
358     }
359   if (tfound)
360     {
361       schedule_last_tfound = tfound;
362       /* send_dbgmsg_fmt ("fnd=%u", ((tfound/6)*6)); */
363       tfound %= 6;
364       /* send_dbgmsg_fmt ("act=%u", tfound); */
365       if (tfound == SCHEDULE_ACTION_UP)
366         {
367           next_action = action_up;
368           send_dbgmsg ("sch-act up");
369         }
370       else if (tfound == SCHEDULE_ACTION_DOWN)
371         {
372           next_action = action_down;
373           send_dbgmsg ("sch-act dn");
374         }
375     }
376 }
377
378
379 /* Process a shutter command.  */
380 static void
381 process_shutter_cmd (byte *msg)
382 {
383   uint16_t val16;
384   byte err = 0;
385
386   switch (msg[6])
387     {
388     case P_H61_SHUTTER_QUERY:
389       {
390         msg[1] = msg[3];
391         msg[2] = msg[4];
392         msg[3] = config.nodeid_hi;
393         msg[4] = config.nodeid_lo;
394         msg[5] |= P_BUSCTL_RESPMASK;
395
396         msg[7] = 0; /* No error.  */
397         msg[8] = shutter_state;
398         memset (msg+9, 0, 7);
399         csma_send_message (msg, MSGSIZE);
400       }
401       break;
402
403     case P_H61_SHUTTER_DRIVE:
404       {
405         if (msg[7] > 1 /* Only all shutters or shutter 1 are allowed.  */
406             || msg[9] || msg[10] || msg[11] || msg[12] || msg[13]
407             || msg[14] || msg[15] /* Reserved bytes are not zero.  */
408             || (msg[8] & 0x10)    /* Reserved bit is set.  */
409             || (msg[8] & 0x20)    /* Not yet supported.  */ )
410           err = 1;
411         else if ((msg[8] & 0xc0) == 0xc0)
412           {
413             next_action = action_up;
414             send_dbgmsg ("bus-act up");
415           }
416         else if ((msg[8] & 0xc0) == 0x80)
417           {
418             next_action = action_down;
419             send_dbgmsg ("bus-act dn");
420           }
421         else
422           err = 1;
423
424         msg[1] = msg[3];
425         msg[2] = msg[4];
426         msg[3] = config.nodeid_hi;
427         msg[4] = config.nodeid_lo;
428         msg[5] |= P_BUSCTL_RESPMASK;
429         msg[7] = err;
430         msg[8] = shutter_state;
431         memset (msg+9, 0, 7);
432         csma_send_message (msg, MSGSIZE);
433       }
434       break;
435
436     case P_H61_SHUTTER_QRY_TIMINGS:
437       break;
438
439     case P_H61_SHUTTER_UPD_TIMINGS:
440       break;
441
442     case P_H61_SHUTTER_QRY_SCHEDULE:
443       {
444         byte i;
445
446         msg[1] = msg[3];
447         msg[2] = msg[4];
448         msg[3] = config.nodeid_hi;
449         msg[4] = config.nodeid_lo;
450         msg[5] |= P_BUSCTL_RESPMASK;
451         msg[7] = 0; /* We only have a global schedule for now.  */
452         msg[8] = 0; /* No error.  */
453         for (i=0; i < DIM (ee_data.u.shutterctl.schedule); i++)
454           {
455             val16 = eeprom_read_word (&ee_data.u.shutterctl.schedule[i]);
456             switch ((val16 % 6))
457               {
458               case SCHEDULE_ACTION_UP:   msg[13] = 0b11000000; break;
459               case SCHEDULE_ACTION_DOWN: msg[13] = 0b10000000; break;
460               default: msg[13] = 0;  break; /* Undefined.  */
461               }
462             val16 /= 6;
463             val16 *= 6;
464             msg[9] = DIM (ee_data.u.shutterctl.schedule);
465             msg[10] = i;
466             msg[11] = val16 >> 8;
467             msg[12] = val16;
468             /* msg[13] already set.  */
469             msg[14] = 0;
470             msg[15] = 0;
471             csma_send_message (msg, MSGSIZE);
472           }
473       }
474       break;
475
476     case P_H61_SHUTTER_UPD_SCHEDULE:
477       if (msg[8] || msg[14] || msg[15] || msg[9] != 1
478           || msg[10] >= DIM (ee_data.u.shutterctl.schedule))
479         {
480           /* Bad message or eeprom reset  */
481           if (msg[7] == 0xf0 && msg[9] == 16 && msg[10] == 0xf0
482               && msg[11] == 0xf0 && msg[12] == 0xf0 && msg[13] == 0xf0)
483             {
484               init_eeprom (1);
485             }
486         }
487       else
488         {
489           /* Get time and round down to the full minute.  */
490           val16 = (msg[11] << 8) | msg[12];
491           val16 /= 6;
492           val16 *= 6;
493           /* Include the action.  Note that SCHEDULE_ACTION_NOP is the
494              default.  */
495           if (msg[13] == 0b11000000)
496             val16 += SCHEDULE_ACTION_UP;
497           else if (msg[13] == 0b10000000)
498             val16 += SCHEDULE_ACTION_DOWN;
499
500           eeprom_write_word (&ee_data.u.shutterctl.schedule[msg[10]], val16);
501         }
502       break;
503
504     default:
505       break;
506     }
507 }
508
509
510
511 /* Process a sensor command.  */
512 static void
513 process_sensor_cmd (byte *msg)
514 {
515   switch (msg[6])
516     {
517     case P_H61_SENSOR_TEMPERATURE:
518       if (sensor_ctrl.active)
519         {
520           /* A second client request, if it is a different one switch
521              to broadcast mode.  */
522           if (msg[3] != sensor_ctrl.addr_hi || msg[4] != sensor_ctrl.addr_lo)
523             {
524               sensor_ctrl.addr_hi = 0xff;
525               sensor_ctrl.addr_lo = 0xff;
526             }
527         }
528       else
529         {
530           sensor_ctrl.active = 5;    /* Number of tries.  */
531           sensor_ctrl.addr_hi = msg[3];
532           sensor_ctrl.addr_lo = msg[4];
533
534           /* Trigger the read out machinery.  */
535           onewire_enable ();
536           onewire_write_byte (0xcc); /* Skip ROM.  */
537           onewire_write_byte (0x44); /* Convert T.  */
538           /* Now we need to wait at least 750ms to read the value from
539              the scratchpad.  */
540           sensor_action_delay = 900; /* ms */
541           sensor_action_event = 0;
542       }
543       break;
544
545     default:
546       break;
547     }
548 }
549
550
551 /* Try to read the result from the sensor and send it back.  This
552    function shall be called at least 750ms after the first conversion
553    message.  */
554 static void
555 send_sensor_result (void)
556 {
557   byte i, crc;
558   int16_t t;
559   byte msg[16]; /* Used to read the scratchpad and to build the message.  */
560
561   if (!sensor_ctrl.active)
562     return;
563
564   onewire_enable ();         /* Reset */
565   onewire_write_byte (0xcc); /* Skip ROM.  */
566   onewire_write_byte (0xbe); /* Read scratchpad.  */
567   for (i=0; i < 9; i++)
568     msg[i] = onewire_read_byte ();
569
570   crc = 0;
571   for (i=0; i < 8; i++)
572     crc = _crc_ibutton_update (crc, msg[i]);
573
574   if (msg[8] == crc)
575     {
576       t = (msg[1] << 8) | msg[0];
577       t = (t*100 - 25 + ((16 - msg[6])*100 / 16)) / 20;
578     }
579   else
580     {
581       t = 0x7fff;  /* Read error */
582     }
583
584   if (t != 0x7fff || !--sensor_ctrl.active)
585     {
586       /* Success or read error with the counter at zero.  */
587       msg[0] = PROTOCOL_EBUS_H61;
588       msg[1] = sensor_ctrl.addr_hi;
589       msg[2] = sensor_ctrl.addr_lo;
590       msg[3] = config.nodeid_hi;
591       msg[4] = config.nodeid_lo;
592       msg[5] = (P_H61_SENSOR | P_H61_RESPMASK);
593       msg[6] = P_H61_SENSOR_TEMPERATURE;
594       msg[7] = (1 << 4 | 1); /* Group 1 of 1.  */
595       msg[8] = (t >> 8); /* Sensor no. 0.  */
596       msg[9] = t;
597       msg[10] = 0x80; /* No sensor no. 1.  */
598       msg[11] = 0;
599       msg[12] = 0x80; /* No sensor no. 2.  */
600       msg[13] = 0;
601       msg[14] = 0x80; /* No sensor no. 3.  */
602       msg[15] = 0;
603       csma_send_message (msg, MSGSIZE);
604
605       sensor_ctrl.active = 0;  /* Ready. */
606       onewire_disable ();
607     }
608   else
609     {
610       send_dbgmsg ("sens #4");
611       /* Better issue the read command again.  */
612       onewire_enable ();         /* Reset.     */
613       onewire_write_byte (0xcc); /* Skip ROM.  */
614       onewire_write_byte (0x44); /* Convert T. */
615       /* Try again ...  */
616       sensor_action_delay = 1100; /*ms*/
617       sensor_action_event = 0;
618     }
619 }
620
621
622
623 /* A new message has been received and we must now parse the message
624    quickly and see what to do.  We need to return as soon as possible,
625    so that the caller may re-enable the receiver.  */
626 static void
627 process_ebus_h61 (byte *msg)
628 {
629   char is_response = !!(msg[5] & P_H61_RESPMASK);
630
631   if (!(msg[1] == config.nodeid_hi && msg[2] == config.nodeid_lo))
632     return; /* Not addressed to us.  */
633
634   switch ((msg[5] & ~P_H61_RESPMASK))
635     {
636     case P_H61_SHUTTER:
637       if (!is_response)
638         process_shutter_cmd (msg);
639       break;
640
641     case P_H61_SENSOR:
642       if (!is_response)
643         process_sensor_cmd (msg);
644       break;
645
646     default:
647       break;
648     }
649 }
650
651
652 /* Process busctl messages.  */
653 static void
654 process_ebus_busctl (byte *msg)
655 {
656   uint16_t val16;
657   byte     val8;
658   char is_response = !!(msg[5] & P_BUSCTL_RESPMASK);
659
660   if (is_response)
661     return;  /* Nothing to do.  */
662   else if (msg[3] == 0xff || msg[4] == 0xff || msg[4] == 0)
663     return ; /* Bad sender address.  */
664   else if (msg[1] == config.nodeid_hi && msg[2] == config.nodeid_lo)
665     ; /* Directed to us.  */
666   else if ((msg[1] == config.nodeid_hi || msg[1] == 0xff) && msg[2] == 0xff)
667     ; /* Broadcast. */
668   else
669     return; /* Not addressed to us.  */
670
671   switch ((msg[5] & ~P_BUSCTL_RESPMASK))
672     {
673     case P_BUSCTL_TIME:
674       {
675         uint16_t t;
676
677         t = get_current_time ();
678         val16 = (msg[7] << 8) | msg[8];
679         val8  = (msg[6] & 0x02)? msg[9] : 0;
680         set_current_fulltime (val16, val8);
681         if (val16 > t)
682           process_schedule (val16, t);
683       }
684       break;
685
686     case P_BUSCTL_QRY_TIME:
687       msg[1] = msg[3];
688       msg[2] = msg[4];
689       msg[3] = config.nodeid_hi;
690       msg[4] = config.nodeid_lo;
691       msg[5] |= P_BUSCTL_RESPMASK;
692       msg[6] = 0; /* fixme: return an error for unknown shutter numbers.  */
693       val16 = get_current_fulltime (&val8);
694       msg[7] = val16 >> 8;
695       msg[8] = val16;
696       msg[9] = val8;
697       memset (msg+10, 0, 6);
698       csma_send_message (msg, MSGSIZE);
699       break;
700
701     case P_BUSCTL_QRY_VERSION:
702       msg[1] = msg[3];
703       msg[2] = msg[4];
704       msg[3] = config.nodeid_hi;
705       msg[4] = config.nodeid_lo;
706       msg[5] |= P_BUSCTL_RESPMASK;
707       msg[6] = eeprom_read_byte (&ee_data.nodetype);
708       msg[7] = 0;
709       memcpy_P (msg+8, PSTR (GIT_REVISION), 7);
710       msg[15] = 0;
711       csma_send_message (msg, MSGSIZE);
712       break;
713
714     case P_BUSCTL_SET_DEBUG:
715       set_debug_flags (msg[6]);
716       break;
717
718     case P_BUSCTL_QRY_DEBUG:
719       msg[1] = msg[3];
720       msg[2] = msg[4];
721       msg[3] = config.nodeid_hi;
722       msg[4] = config.nodeid_lo;
723       msg[5] |= P_BUSCTL_RESPMASK;
724       msg[6] = config.debug_flags;
725       memset (msg+7, 0, 9);
726       csma_send_message (msg, MSGSIZE);
727       break;
728
729     default:
730       break;
731     }
732 }
733
734
735
736 /* Init our eeprom data if needed. */
737 static void
738 init_eeprom (byte force)
739 {
740   uint16_t uptime, downtime;
741   byte i;
742
743   if (force || !eeprom_read_word (&ee_data.u.shutterctl.schedule[0]))
744     {
745       /* The schedule is empty - set up reasonable values for every
746          day of the week.  */
747       uptime = (7 * 60 + 30) * 6 + SCHEDULE_ACTION_UP;
748       downtime = (18 * 60 + 15) * 6 + SCHEDULE_ACTION_DOWN;
749       for (i=0; i < 7*2 && i < DIM (ee_data.u.shutterctl.schedule); i++)
750         {
751           if (i==6*2) /* Pull up on Sundays one hour later.  */
752             uptime += 60 * 6;
753           eeprom_write_word (&ee_data.u.shutterctl.schedule[i], uptime);
754           i++;
755           eeprom_write_word (&ee_data.u.shutterctl.schedule[i], downtime);
756           uptime += 24 * 60 * 6;
757           downtime += 24 * 60 * 6;
758         }
759       for (; i < DIM (ee_data.u.shutterctl.schedule); i++)
760         eeprom_write_word (&ee_data.u.shutterctl.schedule[i], 0);
761     }
762 }
763
764
765 /*
766     Entry point
767  */
768 int
769 main (void)
770 {
771   byte *msg;
772
773   hardware_setup (NODETYPE_SHUTTER);
774   init_eeprom (0);
775
776   /* Port C configuration changes.  Configure motor ports for output
777      and switch them off. */
778   PORTC &= ~(_BV(MOTOR_down_BIT) | _BV(MOTOR_on_BIT));
779   DDRC  |= _BV(MOTOR_down_BIT) | _BV(MOTOR_on_BIT);
780
781   csma_setup ();
782   onewire_setup ();
783
784   sei (); /* Enable interrupts.  */
785
786   for (;;)
787     {
788       set_sleep_mode (SLEEP_MODE_IDLE);
789       while (!wakeup_main)
790         {
791           cli();
792           if (!wakeup_main)
793             {
794               sleep_enable ();
795               sei ();
796               sleep_cpu ();
797               sleep_disable ();
798             }
799           sei ();
800         }
801       wakeup_main = 0;
802
803       if (sensor_action_event)
804         {
805           sensor_action_event = 0;
806           send_sensor_result ();
807         }
808
809       if (ten_seconds_event)
810         {
811           uint16_t t;
812
813           ten_seconds_event = 0;
814
815           t = get_current_time ();
816           if (!(t % 6))
817             {
818               /* Code to run every minute.  */
819               process_schedule (t, 0);
820             }
821         }
822
823
824       msg = csma_get_message ();
825       if (msg)
826         {
827           /* Process the message.  */
828           switch (msg[0])
829             {
830             case PROTOCOL_EBUS_BUSCTL:
831               process_ebus_busctl (msg);
832               break;
833             case PROTOCOL_EBUS_H61:
834               process_ebus_h61 (msg);
835               break;
836             default:
837               /* Ignore all other protocols.  */
838               break;
839             }
840           /* Re-enable the receiver.  */
841           csma_message_done ();
842         }
843     }
844
845 }