Move leds function to hw file. Comply flash api. Add Copyright.
[bertos.git] / boards / ek-lm3s1968 / examples / gps / main.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2010 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  * \author Andrea Righi <arighi@develer.com>
34  *
35  * \brief DevelGPS demo application with gps.
36  */
37
38 #include "hw/hw_led.h"
39
40 #include <cpu/irq.h>
41
42 #include <drv/lcd_rit128x96.h>
43 #include <drv/timer.h>
44 #include <drv/ser.h>
45 #include <drv/flash.h>
46 #include <drv/kbd.h>
47
48 #include <kern/proc.h>
49 #include <kern/sem.h>
50
51 #include <net/nmea.h>
52
53 #include <gfx/font.h>
54 #include <gfx/text.h>
55 #include <gfx/gfx_p.h> /* BM_PLOT() */
56 #include <gui/menu.h>
57 #include <math.h>
58
59 #include "compass.h"
60
61 /* OLED/GUI stuff */
62 #include "logo.c"
63 #define KEY_MASK (K_UP | K_DOWN | K_LEFT | K_RIGHT | K_OK)
64 extern Font font_gohu;
65 static uint8_t raster[RAST_SIZE(LCD_WIDTH, LCD_HEIGHT)];
66 static Bitmap lcd_bitmap;
67
68 #define SCRSVR_TIME 60000
69 static ticks_t scrsvr_timestamp;
70 static bool is_lcd_off;
71
72 /* Serial and NMEA stuff */
73 static Serial ser_port;
74 static nmeap_context_t nmea;
75 static NmeaGga gga;
76 static NmeaRmc rmc;
77 static NmeaVtg vtg;
78 static bool nmea_update;
79
80 static long prev_b;
81 static long lat, lon;
82 static long target_lat, target_lon;
83
84 /* Storage stuff */
85 #define GPS_POS_MAGIC 0xdeadbeef
86 static Flash flash;
87
88 static void flash_load_target(void)
89 {
90         uint32_t magic;
91
92         kfile_seek(&flash.fd, -FLASH_PAGE_SIZE_BYTES, KSM_SEEK_END);
93         kfile_read(&flash.fd, &magic, sizeof(magic));
94         if (magic == GPS_POS_MAGIC)
95         {
96                 kfile_read(&flash.fd, &target_lat, sizeof(target_lat));
97                 kfile_read(&flash.fd, &target_lon, sizeof(target_lon));
98         }
99 }
100
101 static void flash_save_target(void)
102 {
103         const uint32_t magic = GPS_POS_MAGIC;
104
105         kfile_seek(&flash.fd, -FLASH_PAGE_SIZE_BYTES, KSM_SEEK_END);
106         kfile_write(&flash.fd, &magic, sizeof(magic));
107         kfile_write(&flash.fd, &target_lat, sizeof(target_lat));
108         kfile_write(&flash.fd, &target_lon, sizeof(target_lon));
109         kfile_flush(&flash.fd);
110 }
111
112
113 /* Display management */
114 INLINE void video_off(void)
115 {
116         unsigned long delta =
117                 (long)ticks_to_ms(timer_clock_unlocked()) -
118                 (long)scrsvr_timestamp;
119
120         if (!is_lcd_off && delta > SCRSVR_TIME)
121         {
122                 rit128x96_off();
123                 is_lcd_off = true;
124         }
125 }
126
127 INLINE void video_on(void)
128 {
129         if (is_lcd_off)
130         {
131                 rit128x96_on();
132                 is_lcd_off = false;
133         }
134         scrsvr_timestamp = ticks_to_ms(timer_clock_unlocked());
135 }
136
137 INLINE void repaint(Bitmap *bm)
138 {
139         rit128x96_blitBitmap(bm);
140 }
141
142 INLINE keymask_t keypad_peek(void)
143 {
144         keymask_t key = kbd_peek();
145
146         if (key & KEY_MASK)
147         {
148                 if (is_lcd_off)
149                         key = 0;
150                 video_on();
151         }
152         return key;
153 }
154
155 /* Status LED thread */
156 static void NORETURN led_process(void)
157 {
158         while (1)
159         {
160                 video_off();
161                 if (!nmea_update)
162                 {
163                         timer_delay(1000);
164                         continue;
165                 }
166                 LED_ON();
167                 timer_delay(100);
168                 LED_OFF();
169                 nmea_update = false;
170         }
171 }
172
173 /* NMEA parser */
174 static void nmea_callback(nmeap_context_t *context, void *data, void *user_data)
175 {
176         (void)context;
177         (void)data;
178         (void)user_data;
179
180         lat = (long)gga.latitude;
181         lon = (long)gga.longitude;
182
183         nmea_update = true;
184 }
185
186 static void NORETURN ser_process(void)
187 {
188         while (1)
189         {
190                 nmea_poll(&nmea, &ser_port.fd);
191                 kfile_clearerr(&ser_port.fd);
192         }
193 }
194
195 /* Target position screen */
196 static void target(Bitmap *bm)
197 {
198         const long STEP = 10000000,
199                 MAX_LAT = 90000000, MIN_LAT = -90000000,
200                 MAX_LON = 180000000, MIN_LON = -180000000;
201         long step = STEP, target;
202         int row = 0, pos = 1;
203         keymask_t key;
204
205         gfx_bitmapClear(bm);
206         video_on();
207         text_xprintf(bm, 0, 0,
208                 STYLEF_UNDERLINE | TEXT_CENTER | TEXT_FILL,
209                 "Target position");
210         while (1)
211         {
212                 if (!is_lcd_off)
213                 {
214                         text_xprintf(bm, 3, 0, TEXT_FILL,
215                                         "Lat:  %02ld.%06ld %c %s",
216                                         ABS(target_lat) / 1000000L,
217                                         ABS(target_lat) % 1000000,
218                                         target_lat >= 0 ? 'N' : 'S',
219                                         row == 0 ? "<-" : "");
220                         text_xprintf(bm, row ? 4 : 6, 0,
221                                         TEXT_FILL | TEXT_CENTER, " ");
222                         text_xprintf(bm, row ? 6 : 4, 0,
223                                         TEXT_FILL, "%*c",
224                                         (step < 1000000L) ?
225                                                 pos + 7 : pos + 6, '^');
226                         text_xprintf(bm, 5, 0, TEXT_FILL,
227                                         "Lon: %03ld.%06ld %c %s",
228                                         ABS(target_lon) / 1000000L,
229                                         ABS(target_lon) % 1000000L,
230                                         target_lon >= 0 ? 'E' : 'W',
231                                         row == 1 ? "<-" : "");
232                         repaint(bm);
233                 }
234                 key = keypad_peek();
235                 if (key & K_UP)
236                 {
237                         if (row == 0)
238                         {
239                                 target = target_lat + step;
240                                 if (target <= MAX_LAT)
241                                 {
242                                         if (target_lat < 0 && target > 0)
243                                                 target_lat = ABS(target_lat) +
244                                                                 step;
245                                         else
246                                                 target_lat = target;
247                                 }
248                         }
249                         else
250                         {
251                                 target = target_lon + step;
252                                 if (target <= MAX_LON)
253                                 {
254                                         if (target_lon < 0 && target > 0)
255                                                 target_lon = ABS(target_lon) +
256                                                                 step;
257                                         else
258                                                 target_lon = target;
259                                 }
260                         }
261                 }
262                 else if (key & K_DOWN)
263                 {
264                         if (row == 0)
265                         {
266                                 target = target_lat - step;
267                                 if (target >= MIN_LAT)
268                                 {
269                                         if (target_lat > 0 && target < 0)
270                                                 target_lat = -ABS(target_lat) -
271                                                                 step;
272                                         else
273                                                 target_lat = target;
274                                 }
275                         }
276                         else
277                         {
278                                 target = target_lon - step;
279                                 if (target >= MIN_LON)
280                                 {
281                                         if (target_lon > 0 && target < 0)
282                                                 target_lon = -ABS(target_lon) -
283                                                                 step;
284                                         else
285                                                 target_lon = target;
286                                 }
287                         }
288                 }
289                 else if (key & K_LEFT)
290                 {
291                         if (step < STEP)
292                         {
293                                 step *= 10;
294                                 pos--;
295                         }
296                 }
297                 else if (key & K_RIGHT)
298                 {
299                         if (step >= 10)
300                         {
301                                 step /= 10;
302                                 pos++;
303                         }
304                 }
305                 else if (key & K_OK)
306                 {
307                         if (row++ == 1)
308                                 break;
309                          /* Move to longigude */
310                 }
311                 cpu_relax();
312         }
313         flash_save_target();
314 }
315
316 /* Compass management */
317 static void draw_compass(Bitmap *bm)
318 {
319         const int R = LCD_HEIGHT / 3, R_SMALL = 10;
320         long x, y, x1, y1, x2, y2, d;
321         int i;
322
323         d = distance(lat / 1E6, lon / 1E6, target_lat / 1E6, target_lon / 1E6);
324
325         x = (long)((float)R * (1 + sin(deg2rad((float)prev_b))));
326         y = (long)((float)R * (1 - cos(deg2rad((float)prev_b))));
327
328         x1 = R - R_SMALL + (long)((float)R_SMALL *
329                 (1 + sin(deg2rad((float)prev_b - 120.0))));
330         y1 = R - R_SMALL + (long)((float)R_SMALL *
331                 (1 - cos(deg2rad((float)prev_b - 120.0))));
332         x2 = R - R_SMALL + (long)((float)R_SMALL *
333                 (1 + sin(deg2rad((float)prev_b + 120.0))));
334         y2 = R - R_SMALL + (long)((float)R_SMALL *
335                 (1 - cos(deg2rad((float)prev_b + 120.0))));
336
337         gfx_bitmapClear(bm);
338         /* Print direction heading and degrees */
339         text_xprintf(bm, 0, 5, 0, "%s", "N");
340         text_xprintf(bm, 0, 0, TEXT_RIGHT, "%s", compass_heading(prev_b));
341         text_xprintf(bm, 1, 0, TEXT_RIGHT, "%ld deg.", prev_b);
342         /* Print distance */
343         text_xprintf(bm, 2, 0, TEXT_RIGHT, "%ld %s",
344                         d >= 1000 ? d / 1000 : d,
345                         d >= 1000 ? "Km" : "m");
346         /* Print current and target position */
347         text_xprintf(bm, 6, 0, TEXT_FILL, "%02ld.%06ld%c%03ld.%06ld%c",
348                         ABS(lat) / 1000000L,
349                         ABS(lat) % 1000000,
350                         lat >= 0 ? 'N' : 'S',
351                         ABS(lon) / 1000000L,
352                         ABS(lon) % 1000000L,
353                         lon >= 0 ? 'E' : 'W');
354         text_xprintf(bm, 7, 0, TEXT_FILL, "%02ld.%06ld%c%03ld.%06ld%c",
355                         ABS(target_lat) / 1000000L,
356                         ABS(target_lat) % 1000000,
357                         target_lat >= 0 ? 'N' : 'S',
358                         ABS(target_lon) / 1000000L,
359                         ABS(target_lon) % 1000000L,
360                         target_lon >= 0 ? 'E' : 'W');
361         /* Draw the circle */
362         for (i = 0; i < 360; i++)
363                 BM_PLOT(bm,
364                         (long)((float)R * (1 + sin(deg2rad((float)i)))),
365                         (long)((float)R * (1 - cos(deg2rad((float)i)))));
366         /* Draw the needle */
367         gfx_rectFill(bm, R - 2, R - 2, R + 2, R + 2);
368         gfx_line(bm, R, R, x1, y1);
369         gfx_line(bm, R, R, x2, y2);
370         gfx_line(bm, x1, y1, x, y);
371         gfx_line(bm, x2, y2, x, y);
372
373         repaint(bm);
374 }
375
376 static void compass(Bitmap *bm)
377 {
378         long b, inc;
379
380         video_on();
381         while (1)
382         {
383                 if (!is_lcd_off)
384                 {
385                         b = bearing(lat / 1E6, lon / 1E6,
386                                         target_lat / 1E6, target_lon / 1E6);
387                         inc = ABS(b - prev_b) < 360 - ABS(b - prev_b) ?  1 : -1;
388                         /* Compass animation */
389                         if (b < prev_b)
390                                 inc = -inc;
391                         while (prev_b != b)
392                         {
393                                 prev_b = (prev_b + inc) % 360;
394                                 if (prev_b < 0)
395                                         prev_b = 359;
396                                 if (!(prev_b % 5))
397                                         draw_compass(bm);
398                                 if (keypad_peek() & KEY_MASK)
399                                         return;
400                         }
401                         draw_compass(bm);
402                 }
403                 cpu_relax();
404                 if (keypad_peek() & KEY_MASK)
405                         return;
406         }
407 }
408
409 /* GPS receiver status */
410 static const char *gps_fix[] =
411 {
412         "invalid",
413         "GPS",
414         "DGPS",
415         "PPS",
416         "RTK",
417         "float-RTK",
418         "estimated",
419         "manual",
420         "simulation",
421 };
422
423 static void gps_data(Bitmap *bm)
424 {
425         struct tm tm;
426         char buf[32];
427
428         video_on();
429         gfx_bitmapClear(bm);
430         while (1)
431         {
432                 if (!is_lcd_off)
433                 {
434                         if (!rmc.time)
435                         {
436                                 text_xprintf(bm, 3, 0,
437                                         TEXT_CENTER | TEXT_FILL, "No GPS data");
438                         }
439                         else
440                         {
441                                 gmtime_r(&rmc.time, &tm);
442
443                                 text_xprintf(bm, 0, 0, TEXT_FILL,
444                                                 "Lat. %ld.%06ld%c",
445                                                 ABS(lat) / 1000000L,
446                                                 ABS(lat) % 1000000,
447                                                 lat >= 0 ? 'N' : 'S');
448                                 text_xprintf(bm, 1, 0, TEXT_FILL,
449                                                 "Lon. %ld.%06ld%c",
450                                                 ABS(lon) / 1000000L,
451                                                 ABS(lon) % 1000000L,
452                                                 lon >= 0 ? 'E' : 'W');
453                                 text_xprintf(bm, 2, 0, TEXT_FILL,
454                                                 "Alt. %d", gga.altitude);
455                                 text_xprintf(bm, 3, 0, TEXT_FILL,
456                                                 "Speed: %d", vtg.km_speed);
457                                 if (gga.quality < countof(gps_fix))
458                                         text_xprintf(bm, 4, 0, TEXT_FILL,
459                                                         "Fix: %s",
460                                                         gps_fix[gga.quality]);
461                                 else
462                                         text_xprintf(bm, 4, 0, TEXT_FILL,
463                                                         "Fix: %d", gga.quality);
464                                 text_xprintf(bm, 5, 0, TEXT_FILL,
465                                                 "Satellites: %d",
466                                                 gga.satellites);
467                                 strftime(buf, sizeof(buf),
468                                                 "Date: %Y-%m-%d %a", &tm);
469                                 text_xprintf(bm, 6, 0, TEXT_FILL, "%s", buf);
470                                 strftime(buf, sizeof(buf),
471                                                 "Time: %H:%M:%S (UTC)", &tm);
472                                 text_xprintf(bm, 7, 0, TEXT_FILL, "%s", buf);
473                         }
474                         repaint(bm);
475                 }
476                 if (keypad_peek() & KEY_MASK)
477                         break;
478                 cpu_relax();
479         }
480 }
481
482 /* BeRTOS screen */
483 static void about(Bitmap *bm)
484 {
485         gfx_bitmapClear(bm);
486         video_on();
487         text_xprintf(bm, 7, 0,
488                 STYLEF_UNDERLINE | TEXT_CENTER | TEXT_FILL,
489                 "http://www.bertos.org");
490         repaint(bm);
491         if (!is_lcd_off)
492         {
493                 const uint8_t *p = &logo[BITMAP_HEADER_SIZE];
494                 uint8_t h = logo[BITMAP_HEIGHT_OFFSET];
495                 uint8_t w = logo[BITMAP_WIDTH_OFFSET];
496                 uint8_t r;
497
498                 for (r = 0; r < h; r++)
499                 {
500                         rit128x96_blitRaw(p,
501                                 (128 - w) / 2, 70 - r, w, 1);
502                         p += w / 2;
503                 }
504         }
505         while (!(keypad_peek() & KEY_MASK))
506                 cpu_relax();
507 }
508
509 static struct MenuItem main_items[] =
510 {
511         {(const_iptr_t)"Target", 0, (MenuHook)target, (iptr_t)&lcd_bitmap},
512         {(const_iptr_t)"Compass", 0, (MenuHook)compass, (iptr_t)&lcd_bitmap},
513         {(const_iptr_t)"GPS data", 0, (MenuHook)gps_data, (iptr_t)&lcd_bitmap},
514         {(const_iptr_t)"About...", 0, (MenuHook)about, (iptr_t)&lcd_bitmap},
515         {(const_iptr_t)0, 0, NULL, (iptr_t)NULL}
516 };
517
518 static struct Menu main_menu =
519 {
520         main_items, "DevelGPS v0.1", MF_STICKY | MF_SAVESEL, &lcd_bitmap, 0
521 };
522
523 static void init(void)
524 {
525         IRQ_ENABLE;
526
527         kdbg_init();
528         timer_init();
529         proc_init();
530
531         scrsvr_timestamp = ticks_to_ms(timer_clock_unlocked());
532         LED_INIT();
533
534         flash_init(&flash);
535         flash_load_target();
536
537         ser_init(&ser_port, SER_UART1);
538         ser_setbaudrate(&ser_port, 38400);
539
540         nmeap_init(&nmea, NULL);
541         nmeap_addParser(&nmea, "GPGGA", nmea_gpgga, nmea_callback, &gga);
542         nmeap_addParser(&nmea, "GPRMC", nmea_gprmc, nmea_callback, &rmc);
543         nmeap_addParser(&nmea, "GPVTG", nmea_gpvtg, nmea_callback, &vtg);
544
545         rit128x96_init();
546         gfx_bitmapInit(&lcd_bitmap, raster, LCD_WIDTH, LCD_HEIGHT);
547         gfx_setFont(&lcd_bitmap, &font_gohu);
548         repaint(&lcd_bitmap);
549
550         kbd_init();
551 }
552
553 int main(void)
554 {
555         init();
556
557         proc_new(led_process, NULL, KERN_MINSTACKSIZE * 2, NULL);
558         proc_new(ser_process, NULL, KERN_MINSTACKSIZE * 2, NULL);
559
560         while (1)
561         {
562                 iptr_t res = menu_handle(&main_menu);
563                 if (res != MENU_TIMEOUT)
564                         video_on();
565                 cpu_relax();
566         }
567 }