Contiki 3.x
raven-lcd.c
Go to the documentation of this file.
1 /* Copyright (c) 2008, Swedish Institute of Computer Science
2  * All rights reserved.
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * * Neither the name of the copyright holders nor the names of
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  *
31 */
32 
33 /** \addtogroup raven
34  * @{
35  */
36 
37 /**
38  * \defgroup ravenserial Serial interface between Raven processors
39  *
40  * \brief This module contains code to interface a Contiki-based
41  * project on the AVR Raven platform's ATMega1284P chip to the LCD
42  * driver chip (ATMega3290P) on the Raven.
43  *
44  * \author Blake Leverett <bleverett@gmail.com>
45  * \author Cristiano De Alti <cristiano_dealti@hotmail.com>
46  *
47  * @{
48  */
49 /**
50  * \file
51  * This file contains code to connect the two AVR Raven processors via a serial connection.
52  *
53  */
54 
55 #define DEBUG 0 //Making this 1 will slightly alter command timings
56 #if DEBUG
57 #define PRINTF(FORMAT,args...) printf_P(PSTR(FORMAT),##args)
58 #else
59 #define PRINTF(...)
60 #endif
61 #define DEBUGSERIAL 0 //Making this 1 will significantly alter command timings
62 
63 #include "contiki.h"
64 #include "contiki-lib.h"
65 #include "contiki-net.h"
66 
67 #if AVR_WEBSERVER
68 #include "webserver-nogui.h"
69 #include "httpd-cgi.h"
70 #endif
71 
72 #include "raven-lcd.h"
73 #include "lib/sensors.h"
74 
75 #include <string.h>
76 #include <stdio.h>
77 #include <avr/pgmspace.h>
78 #include <avr/eeprom.h>
79 #include <avr/sleep.h>
80 #include <dev/watchdog.h>
81 
82 static uint8_t count = 0;
83 static uint8_t seqno;
84 uip_ipaddr_t dest_addr;
85 
86 #define MAX_CMD_LEN 20
87 static struct{
88  uint8_t frame[MAX_CMD_LEN];
89  uint8_t ndx;
90  uint8_t len;
91  uint8_t cmd;
92  uint8_t done;
93 } cmd;
94 
95 #define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
96 #define UIP_ICMP_BUF ((struct uip_icmp_hdr *)&uip_buf[uip_l2_l3_hdr_len])
97 #define PING6_DATALEN 16
98 
99 static int battery_value;
100 static int temperature_value;
101 
102 void rs232_send(uint8_t port, unsigned char c);
103 
104 /*---------------------------------------------------------------------------*/
105 /* Sends a ping packet out the radio */
106 /* Useful for debugging so allow external calls */
107 void
108 raven_ping6(void)
109 {
110 #define PING_GOOGLE 0
111 
112  UIP_IP_BUF->vtc = 0x60;
113  UIP_IP_BUF->tcflow = 1;
114  UIP_IP_BUF->flow = 0;
115  UIP_IP_BUF->proto = UIP_PROTO_ICMP6;
116  UIP_IP_BUF->ttl = uip_ds6_if.cur_hop_limit;
117 #if PING_GOOGLE
118  if (seqno==1) {
119  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, uip_ds6_defrt_choose()); //the default router
120  } else if (seqno==2) {
121  uip_ip6addr(&UIP_IP_BUF->destipaddr,0x2001,0x4860,0x800f,0x0000,0x0000,0x0000,0x0000,0x0093); //ipv6.google.com
122  } else if (seqno==3) {
123  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, uip_ds6_defrt_choose()); //the default router
124  } else {
125 // uip_ip6addr(&UIP_IP_BUF->destipaddr,0x2001,0x0420,0x5FFF,0x007D,0x02D0,0xB7FF,0xFE23,0xE6DB); //?.cisco.com
126  uip_ip6addr(&UIP_IP_BUF->destipaddr,0x2001,0x0420,0x0000,0x0010,0x0250,0x8bff,0xfee8,0xf800); //six.cisco.com
127  }
128 #else
129  uip_ipaddr_copy(&UIP_IP_BUF->destipaddr, uip_ds6_defrt_choose()); //the default router
130 #endif
131 
132  uip_ds6_select_src(&UIP_IP_BUF->srcipaddr, &UIP_IP_BUF->destipaddr);
134  UIP_ICMP_BUF->icode = 0;
135  /* set identifier and sequence number to 0 */
136  memset((void *)UIP_ICMP_BUF + UIP_ICMPH_LEN, 0, 4);
137  /* put one byte of data */
138  memset((void *)UIP_ICMP_BUF + UIP_ICMPH_LEN + UIP_ICMP6_ECHO_REQUEST_LEN,
139  count, PING6_DATALEN);
140 
141 
142  uip_len = UIP_ICMPH_LEN + UIP_ICMP6_ECHO_REQUEST_LEN + UIP_IPH_LEN + PING6_DATALEN;
143  UIP_IP_BUF->len[0] = (uint8_t)((uip_len - 40) >> 8);
144  UIP_IP_BUF->len[1] = (uint8_t)((uip_len - 40) & 0x00FF);
145 
146  UIP_ICMP_BUF->icmpchksum = 0;
147  UIP_ICMP_BUF->icmpchksum = ~uip_icmp6chksum();
148 
149 
151 }
152 
153 /*---------------------------------------------------------------------------*/
154 /* Send a serial command frame to the ATMega3290 Processsor on Raven via serial port */
155 static void
156 send_frame(uint8_t cmd, uint8_t len, uint8_t *payload)
157 {
158  uint8_t i;
159 
160  rs232_send(0, SOF_CHAR); /* Start of Frame */
161  rs232_send(0, len);
162  rs232_send(0, cmd);
163  for (i=0;i<len;i++)
164  rs232_send(0,*payload++);
165  rs232_send(0, EOF_CHAR);
166 }
167 char serial_char_received;
168 /*---------------------------------------------------------------------------*/
169 /* Sleep for howlong seconds, or until UART interrupt if howlong==0.
170  * Uses TIMER2 with external 32768 Hz crystal to sleep in 1 second multiples.
171  * TIMER2 may have already been set up for CLOCK_CONF_SECOND ticks/second in clock.c
172 
173  *
174  * Until someone figures out how to get UART to wake from powerdown,
175  * a three second powersave cycle is used with exit based on any character received.
176 
177  * The system clock is adjusted to reflect the sleep time.
178  */
179 
180 void micro_sleep(uint8_t howlong)
181 {
182  uint8_t saved_sreg = SREG, saved_howlong = howlong;
183 #if AVR_CONF_USE32KCRYSTAL
184 /* Save TIMER2 configuration if clock.c is using it */
185  uint8_t savedTCNT2=TCNT2, savedTCCR2A=TCCR2A, savedTCCR2B = TCCR2B, savedOCR2A = OCR2A;
186 #endif
187 
188 // if (howlong==0) {
189 // set_sleep_mode(SLEEP_MODE_PWR_DOWN); // UART can't wake from powerdown
190 // } else {
191  set_sleep_mode(SLEEP_MODE_PWR_SAVE); // Sleep for howlong seconds
192  if (howlong==0) howlong=3; // 3*32/(32768/1024) = 3 second sleep cycle if not specified
193  cli(); // Disable interrupts for the present
194  ASSR |= (1 << AS2); // Set TIMER2 asyncronous from external crystal
195  TCCR2A =(1<<WGM21); // CTC mode
196  TCCR2B =((1<<CS22)|(1<<CS21)|(1<<CS20));// Prescale by 1024 = 32 ticks/sec
197 // while(ASSR & (1 << TCR2BUB)); // Wait for TCNT2 write to finish.
198  OCR2A = howlong*32; // Set TIMER2 output compare register
199 // while(ASSR & (1 << OCR2AUB)); // Wait for OCR2 write to finish.
200  SREG = saved_sreg; // Restore interrupt state.
201 // UCSR(USART,B)&= ~(1<<RXCIE(USART)) // Disable the RX Complete interrupt;
202 // UCSR0B|=(1<<RXCIE0); // Enable UART0 RX complete interrupt
203 // UCSR1B|=(1<<RXCIE1); // Enable UART1 RX complete interrupt
204 // TCNT2 = 0; // Reset TIMER2 timer counter value.
205 // while(ASSR & (1 << TCN2UB)); // Wait for TCNT2 write to finish before entering sleep.
206 // TIMSK2 |= (1 << OCIE2A); // Enable TIMER2 output compare interrupt.
207 // }
208 
209  TCNT2 = 0; // Reset timer
210  watchdog_stop(); // Silence annoying distractions
211  while(ASSR & (1 << TCN2UB)); // Wait for TCNT2 write to (which assures TCCR2x and OCR2A are finished!)
212  TIMSK2 |= (1 << OCIE2A); // Enable TIMER2 output compare interrupt
213  SMCR |= (1 << SE); // Enable sleep mode.
214  while (1) {
215 // TCNT2 = 0; // Cleared automatically in CTC mode
216 // while(ASSR & (1 << TCN2UB)); // Wait for TCNT2 write to finish before entering sleep.
217  serial_char_received=0; // Set when chars received by UART
218  sleep_mode(); // Sleep
219 
220  /* Adjust clock.c for the time spent sleeping */
221  clock_adjust_ticks(howlong * CLOCK_SECOND);
222 
223 // if (TIMSK2&(1<<OCIE2A)) break; // Exit sleep if not awakened by TIMER2
224  PRINTF(".");
225  if (saved_howlong) break; // Exit sleep if nonzero time specified
226 // PRINTF("%d",serial_char_received);
227  if (serial_char_received) break;
228  }
229 
230  SMCR &= ~(1 << SE); //Disable sleep mode after wakeup
231 
232 #if AVR_CONF_USE32KCRYSTAL
233 /* Restore clock.c configuration */
234 // OCRSetup();
235  cli();
236  TCCR2A = savedTCCR2A;
237  TCCR2B = savedTCCR2B;
238  OCR2A = savedOCR2A;
239  TCNT2 = savedTCNT2;
240  sei();
241 #else
242  TIMSK2 &= ~(1 << OCIE2A); //Disable TIMER2 interrupt
243 #endif
244 
245  watchdog_start();
246 }
247 #if !AVR_CONF_USE32KCRYSTAL
248 /*---------------------------------------------------------------------------*/
249 /* TIMER2 Interrupt service */
250 
251 ISR(TIMER2_COMPA_vect)
252 {
253 // TIMSK2 &= ~(1 << OCIE2A); //Just one interrupt needed for waking
254 }
255 #endif /* !AVR_CONF_USE32KCRYSTAL */
256 
257 #if DEBUGSERIAL
258 uint8_t serialcount;
259 char dbuf[30];
260 #endif
261 
262 /*---------------------------------------------------------------------------*/
263 static uint8_t
264 raven_gui_loop(process_event_t ev, process_data_t data)
265 {
266  uint8_t i,activeconnections,radio_state;
267 
268 // PRINTF("\nevent %d ",ev);
269 #if DEBUGSERIAL
270  printf_P(PSTR("Buffer [%d]="),serialcount);
271  serialcount=0;
272  for (i=0;i<30;i++) {
273  printf_P(PSTR(" %d"),dbuf[i]);
274  dbuf[i]=0;
275  }
276 #endif
277  if(ev == tcpip_icmp6_event) switch(*((uint8_t *)data)) {
278 
279 // case ICMP6_NS:
280  /*Tell the 3290 we are being solicited. */
281 // send_frame(REPORT_NS,...);
282 // break; //fall through for beep
283 // case ICMP6_RA:
284  /*Tell the 3290 we have a router. */
285 // send_frame(REPORT_NA,...);
286 // break; //fall through for beep
287  case ICMP6_ECHO_REQUEST:
288  /* We have received a ping request over the air. Tell the 3290 */
289  send_frame(REPORT_PING_BEEP, 0, 0);
290  break;
291  case ICMP6_ECHO_REPLY:
292  /* We have received a ping reply over the air. Send frame back to 3290 */
293  send_frame(REPORT_PING, 1, &seqno);
294  break;
295 
296  } else switch (ev) {
297  case SERIAL_CMD:
298  /* Check for command from serial port, execute it. */
299  /* Note cmd frame is written in an interrupt - delays here can cause overwriting by next command */
300  PRINTF("\nCommand %d length %d done %d",cmd.cmd,cmd.len,cmd.done);
301  if (cmd.done){
302  /* Execute the waiting command */
303  switch (cmd.cmd){
304  case SEND_PING:
305  /* Send ping request over the air */
306  seqno = cmd.frame[0];
307  raven_ping6();
308  break;
309  case SEND_TEMP:
310 #if AVR_WEBSERVER
311  /* Set temperature string in web server */
312  web_set_temp((char *)cmd.frame);
313 #endif
314  temperature_value = atoi((char *)cmd.frame);
315  break;
316  case SEND_ADC2:
317 #if AVR_WEBSERVER
318  /* Set ext voltage string in web server */
319  web_set_voltage((char *)cmd.frame);
320 #endif
321  battery_value = atoi((char *)cmd.frame);
322  break;
323  case SEND_SLEEP:
324  /* Sleep radio and 1284p. */
325  if (cmd.frame[0]==0) { //Time to sleep in seconds
326  /* Unconditional sleep. Don't wake until a serial interrupt. */
327  } else {
328  /* Sleep specified number of seconds (3290p "DOZE" mode) */
329  /* It sleeps a bit longer so we will be always be awake for the next sleep command. */
330 #if UIP_CONF_TCP
331  /* Only sleep this cycle if no active TCP/IP connections, for fast browser responsed */
332  activeconnections=0;
333  for(i = 0; i < UIP_CONNS; ++i) {
334  if((uip_conns[i].tcpstateflags & UIP_TS_MASK) != UIP_CLOSED) activeconnections++;
335  }
336  if (activeconnections) {
337  PRINTF("\nWaiting for %d connections",activeconnections);
338  break;
339  }
340 #endif
341  }
342  radio_state = NETSTACK_RADIO.off();
343  PRINTF ("\nsleep %d radio state %d...",cmd.frame[0],radio_state);
344 
345  /*Sleep for specified time*/
346  PRINTF("\nSleeping...");
347  micro_sleep(cmd.frame[0]);
348 
349  radio_state = NETSTACK_RADIO.on();
350  if (radio_state > 0) {
351  PRINTF("Awake!");
352  } else {
353  PRINTF("Radio wake error %d\n",radio_state);
354  }
355  break;
356  case SEND_WAKE:
357  /* 3290p requests return message showing awake status */
358  send_frame(REPORT_WAKE, 0, 0);
359  break;
360  default:
361  break;
362  }
363  /* Reset command done flag. */
364  cmd.done = 0;
365  }
366  break;
367  default:
368  break;
369  }
370  return 0;
371 }
372 
373 /*---------------------------------------------------------------------------*/
374 /* Process an input character from serial port.
375  * ** This is called from an ISR!!
376 */
377 
378 int raven_lcd_serial_input(unsigned char ch)
379 {
380  /* Tell sleep routine if a reception occurred */
381  /* Random nulls occur for some reason, so ignore those */
382  if (ch) serial_char_received++;
383 #if DEBUGSERIAL
384  if (serialcount<25) dbuf[serialcount]=ch;
385  serialcount++;
386 #endif
387  /* Don't overwrite an unprocessed command */
388 // if (cmd.done) return 0;
389 
390  /* Parse frame, */
391  switch (cmd.ndx){
392  case 0:
393  /* first byte, must be 0x01 */
394  if (ch == 0x01){
395 // cmd.done = false;
396  } else {
397 #if DEBUGSERIAL
398  dbuf[25]++;
399 #endif
400  return 0;
401  }
402  break;
403  case 1:
404  /* Second byte, length of payload */
405  cmd.len = ch;
406  break;
407  case 2:
408  /* Third byte, command byte */
409  cmd.cmd = ch;
410  break;
411  default:
412  /* Payload and ETX */
413  if (cmd.ndx >= (MAX_CMD_LEN+3)) { //buffer overflow!
414  cmd.ndx=0;
415 #if DEBUGSERIAL
416  dbuf[26]++;
417 #endif
418  return 0;
419  }
420  if (cmd.ndx >= cmd.len+3){
421  /* all done, check ETX */
422  if (ch == 0x04){
423  cmd.done = 1;
424 #if DEBUGSERIAL
425  dbuf[27]++;
426 #endif
427  process_post(&raven_lcd_process, SERIAL_CMD, 0);
428  } else {
429  /* Failed ETX */
430 #if DEBUGSERIAL
431  dbuf[28]++;
432 #endif
433  }
434  cmd.ndx=0; //set up for next command
435  return 0;
436  } else {
437  /* Just grab and store payload */
438  cmd.frame[cmd.ndx - 3] = ch;
439  }
440  break;
441  }
442 
443  cmd.ndx++;
444  return 0;
445 }
446 
447 /*---------------------------------------------------------------------------*/
448 void
449 raven_lcd_show_text(char *text) {
450  uint8_t textlen=strlen(text)+1;
451  if (textlen > MAX_CMD_LEN) textlen=MAX_CMD_LEN;
452  send_frame(REPORT_TEXT_MSG, textlen, (uint8_t *) text);
453 }
454 
455 #if AVR_WEBSERVER
456 static void
457 lcd_show_servername(void) {
458 
459 //extern uint8_t eemem_mac_address[8]; //These are defined in httpd-fsdata.c via makefsdata.h
460 extern uint8_t eemem_server_name[16];
461 //extern uint8_t eemem_domain_name[30];
462 char buf[sizeof(eemem_server_name)+1];
463  eeprom_read_block (buf,eemem_server_name, sizeof(eemem_server_name));
464  buf[sizeof(eemem_server_name)]=0;
465  raven_lcd_show_text(buf); //must fit in all the buffers or it will be truncated!
466 }
467 #endif
468 
469 /*---------------------------------------------------------------------------*/
470 int
471 value_temperature(int type) {
472  return temperature_value;
473 }
474 
475 int
476 value_battery(int type) {
477  return battery_value;
478 }
479 
480 /*---------------------------------------------------------------------------*/
481 int
482 configure(int type, int value) {
483  /* prevent compiler warnings */
484  return type = value = 1;
485 }
486 
487 /*---------------------------------------------------------------------------*/
488 int
489 status(int type) {
490  switch(type) {
491  case SENSORS_ACTIVE:
492  case SENSORS_READY:
493  return 1;
494  }
495  return 0;
496 }
497 
498 /*---------------------------------------------------------------------------*/
499 SENSORS_SENSOR(temperature_sensor, "Temperature",
500  value_temperature, configure, status);
501 SENSORS_SENSOR(battery_sensor, "Battery",
502  value_battery, configure, status);
503 
504 /*---------------------------------------------------------------------------*/
505 PROCESS(raven_lcd_process, "Raven LCD interface process");
506 PROCESS_THREAD(raven_lcd_process, ev, data)
507 {
508 
509  PROCESS_BEGIN();
510 
511 #if AVR_WEBSERVER
512  lcd_show_servername();
513 #endif
514 
515  /* Get ICMP6 callbacks from uip6 stack, perform 3290p action on pings, responses, etc. */
516  if(icmp6_new(NULL) == 0) {
517 
518  while(1) {
519  PROCESS_YIELD();
520 // if (ev != ?) //trap frequent strobes?
521  raven_gui_loop(ev, data);
522  }
523  }
524  PROCESS_END();
525 }
526 
527 /** @} */
528 /** @} */
#define ICMP6_ECHO_REPLY
Echo reply.
Definition: uip-icmp6.h:58
uip_len
The length of the packet in the uip_buf buffer.
Definition: tcp_loader.c:75
#define EOF_CHAR
End-of-frame character.
Definition: uart.h:50
#define UIP_CONNS
The maximum number of simultaneously open TCP connections.
Definition: uipopt.h:419
#define ICMP6_ECHO_REQUEST
Echo request.
Definition: uip-icmp6.h:57
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
#define PROCESS(name, strname)
Declare a process.
Definition: process.h:307
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition: process.h:273
uint16_t uip_icmp6chksum(void)
Calculate the ICMP checksum of the packet in uip_buf.
Definition: uip.c:340
#define UIP_ICMP6_ECHO_REQUEST_LEN
Echo Request constant part length.
Definition: uip-icmp6.h:99
void watchdog_stop(void)
Stops the WDT such that it won't timeout and cause MCU reset.
Definition: watchdog.c:58
#define uip_ipaddr_copy(dest, src)
Copy an IP address from one place to another.
Definition: uip.h:1027
#define UIP_IP_BUF
Pointer to IP header.
Definition: uip-nd6.c:104
int process_post(struct process *p, process_event_t ev, process_data_t data)
Post an asynchronous event.
Definition: process.c:322
void uip_ds6_select_src(uip_ipaddr_t *src, uip_ipaddr_t *dst)
Source address selection, see RFC 3484.
Definition: uip-ds6.c:504
void rs232_send(uint8_t port, unsigned char c)
Print a character on RS232.
Definition: rs232.c:325
void tcpip_ipv6_output(void)
This function does address resolution and then calls tcpip_output.
Definition: tcpip.c:530
#define NULL
The null pointer.
#define PROCESS_YIELD()
Yield the currently running process.
Definition: process.h:164
uint8_t icmp6_new(void *appstate)
register an ICMPv6 callback
Definition: tcpip.c:342
process_event_t tcpip_icmp6_event
The ICMP6 event.
Definition: tcpip.c:82
#define UIP_ICMP_BUF
Pointer to ICMP header.
Definition: uip-nd6.c:105
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
#define uip_ip6addr(addr, addr0, addr1, addr2, addr3, addr4, addr5, addr6, addr7)
Construct an IPv6 address from eight 16-bit words.
Definition: uip.h:970
static volatile clock_time_t count
These routines define the AVR-specific calls declared in /core/sys/clock.h CLOCK_SECOND is the number...
Definition: clock.c:80
void clock_adjust_ticks(clock_time_t howmany)
Adjust the system current clock time.
Definition: clock.c:289
uip_ds6_netif_t uip_ds6_if
The single interface.
Definition: uip-ds6.c:71
#define SOF_CHAR
Start-of-frame character.
Definition: uart.h:49
void watchdog_start(void)
Starts the WDT in watchdog mode if enabled by user configuration, maximum interval.
Definition: watchdog.c:49
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120