├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── cgi.c ├── cgi.h ├── files ├── .gitignore ├── .htaccess ├── Makefile ├── README.md ├── ajax │ ├── command.json │ ├── snapshot.jpg │ ├── sysinfo.json │ ├── upgrade.json │ ├── version.json │ └── video.mjpg ├── calibration.html ├── css │ ├── leaflet.css │ └── styles.css ├── data │ └── apm.pdef.xml ├── embed.py ├── filesystem.html ├── gen_manifest.sh ├── gen_version.sh ├── images │ ├── greencopter.png │ ├── home.svg │ ├── main_logo.png │ └── redcopter.png ├── index.html ├── js │ ├── L.TileLayer.PouchDBCached.js │ ├── charts.js │ ├── config.js │ ├── cors.js │ ├── database.js │ ├── leaflet.js │ ├── leaflet.rotatedMarker.js │ ├── mavlink.js │ ├── pouchdb.js │ ├── smoothie.min.js │ ├── tabs.js │ └── ublox.js ├── map.html ├── nvram.html ├── parameters.html ├── status.html ├── system.html └── upgrade.html ├── functions.c ├── functions.h ├── html ├── manifest.appcache └── version.h ├── includes.h ├── linux ├── includes.h ├── mavlink_linux.c ├── mavlink_linux.h ├── util_linux.c └── util_linux.h ├── mavlink_core.c ├── mavlink_core.h ├── mavlink_json.c ├── mavlink_json.h ├── posix ├── functions.c └── functions.h ├── template.c ├── template.h ├── web_files.c ├── web_files.h ├── web_server.c └── web_server.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | generated 3 | web_server 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "modules/mavlink"] 2 | path = modules/mavlink 3 | url = git://github.com/ArduPilot/mavlink 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-Wall -g -Werror -std=gnu99 3 | 4 | SRC = $(wildcard *.c) $(wildcard lib/*.c) $(wildcard linux/*.c) $(wildcard posix/*.c) 5 | OBJ = $(SRC:%.c=%.o) 6 | LIBS = -ltalloc -lpthread 7 | 8 | all: files/embedded.c mavlink web_server 9 | 10 | .PHONY: mavlink 11 | 12 | mavlink: generated/mavlink/ardupilotmega/mavlink.h 13 | 14 | generated/mavlink/ardupilotmega/mavlink.h: 15 | modules/mavlink/pymavlink/tools/mavgen.py --lang C modules/mavlink/message_definitions/v1.0/ardupilotmega.xml -o generated/mavlink --wire-protocol=2.0 16 | 17 | web_server: $(OBJ) files/embedded.c 18 | $(CC) -o web_server $(OBJ) $(LIBS) 19 | 20 | files/embedded.c: 21 | cd files && make 22 | 23 | clean: 24 | rm -f *.o */*.o web_server files/embedded.c 25 | rm -rf generated 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a web server for ArduPilot. It provides the following 2 | features: 3 | 4 | - listens for HTTP requests on specified port 5 | - connects to a mavlink serial port 6 | - forwards mavlink packets to UDP port 14550 broadcast 7 | - provides web interface for parameters, sensor status and map 8 | 9 | Typical usage: 10 | 11 | ./web_server -p 80 -s /dev/serial/by-id/usb-3D_Robotics_PX4_FMU_v2.x_0-if00 12 | 13 | then connect to http://127.0.0.1/ 14 | 15 | Some information on the JSON protocol used is here: 16 | 17 | https://docs.google.com/document/d/12IQFXDRIif06BiriHSCGdiJGZ6zsQ_phQsG_iI6_MAo/edit?usp=sharing 18 | 19 | #### Build Notes 20 | _Ubuntu 16.04_ 21 | - libtalloc-dev is required to build this project. You can install the required package with the following command: `sudo apt-get install libtalloc-dev` 22 | 23 | 24 | -------------------------------------------------------------------------------- /cgi.c: -------------------------------------------------------------------------------- 1 | /* 2 | some simple CGI helper routines 3 | Copyright (C) Andrew Tridgell 1997-2001 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 | */ 19 | 20 | 21 | #include "includes.h" 22 | #include 23 | 24 | #ifdef _POSIX_VERSION 25 | #include "posix/functions.h" 26 | #endif 27 | 28 | #define CONTENT_DISPOSITION "Content-Disposition:" 29 | #define CONTENT_TYPE "Content-Type:" 30 | #define MULTIPART_FORM_DATA "multipart/form-data" 31 | #define CRLF "\r\n" 32 | 33 | /* 34 | trim the tail of a string 35 | */ 36 | void trim_tail(char *s, char *trim_chars) 37 | { 38 | int len = strlen(s); 39 | while (len > 0 && strchr(trim_chars, s[len-1])) len--; 40 | s[len] = 0; 41 | } 42 | 43 | 44 | 45 | /* 46 | inplace handling of + and % escapes in http variables 47 | */ 48 | static void unescape(char *p) 49 | { 50 | unsigned v; 51 | 52 | while (*p) { 53 | if (*p == '+') *p = ' '; 54 | if (*p == '%' && sscanf(p+1, "%02x", &v) == 1 && p[2] != 0) { 55 | *p = (char)v; 56 | memcpy(p+1, p+3, strlen(p+3)+1); 57 | } 58 | p++; 59 | } 60 | } 61 | 62 | /* 63 | read from CGI socket, with buffering 64 | */ 65 | static ssize_t cgi_read(struct cgi_state *cgi, char *buf, uint32_t len) 66 | { 67 | ssize_t ret = 0; 68 | while (len > 0) { 69 | if (cgi->bufofs < cgi->buflen) { 70 | uint16_t n = cgi->buflen - cgi->bufofs; 71 | if (n > len) { 72 | n = len; 73 | } 74 | memcpy(buf, &cgi->buf[cgi->bufofs], n); 75 | buf += n; 76 | cgi->bufofs += n; 77 | len -= n; 78 | ret += n; 79 | } else { 80 | ssize_t n = read(cgi->sock->fd, &cgi->buf[0], sizeof(cgi->buf)); 81 | if (n <= 0) { 82 | break; 83 | } 84 | cgi->buflen = n; 85 | cgi->bufofs = 0; 86 | } 87 | } 88 | return ret; 89 | } 90 | 91 | /* 92 | read one line from a file, allocating space as need be 93 | adjust length on return 94 | */ 95 | static char *grab_line(struct cgi_state *cgi, const char *terminator, unsigned *length) 96 | { 97 | int len = 1024; 98 | char *ret = talloc_size(cgi, len); 99 | int i = 0; 100 | int tlen = strlen(terminator); 101 | 102 | while (*length) { 103 | char c; 104 | 105 | if (i == len) { 106 | len *= 2; 107 | ret = talloc_realloc_size(cgi, ret, len); 108 | } 109 | 110 | if (cgi_read(cgi, &c, 1) != 1) { 111 | (*length) = 0; 112 | break; 113 | } 114 | 115 | (*length)--; 116 | 117 | ret[i++] = c; 118 | 119 | if (i >= tlen && memcmp(terminator, &ret[i-tlen], tlen) == 0) { 120 | i -= tlen; 121 | break; 122 | } 123 | } 124 | 125 | ret[i] = 0; 126 | return ret; 127 | } 128 | 129 | 130 | /* 131 | add a name/value pair to the list of cgi variables 132 | */ 133 | static void put(struct cgi_state *cgi, const char *name, const char *value) 134 | { 135 | struct cgi_var *var; 136 | int len; 137 | char *p; 138 | 139 | if (!name || !value) return; 140 | 141 | web_debug(4, "cgi %s=%s\n", name, value); 142 | 143 | var = talloc(cgi, struct cgi_var); 144 | memset(var, 0, sizeof(*var)); 145 | var->next = cgi->variables; 146 | 147 | /* trim leading spaces */ 148 | while (*name && (*name == '+' || *name == ' ')) name++; 149 | 150 | var->name = talloc_strdup(var, name); 151 | var->value = talloc_strdup(var, value); 152 | unescape(var->name); 153 | unescape(var->value); 154 | 155 | /* trim trailing spaces */ 156 | len = strlen(var->value); 157 | while (len && isspace(var->value[len-1])) { 158 | var->value[len-1] = 0; 159 | len--; 160 | } 161 | 162 | for (p=var->name; *p; p++) { 163 | if (!isalnum(*p) && !strchr("_-", *p)) { 164 | *p = '_'; 165 | } 166 | } 167 | 168 | cgi->variables = var; 169 | char cgi_name[100]; 170 | snprintf(cgi_name, sizeof(cgi_name)-1, "CGI_%s", var->name); 171 | cgi_name[sizeof(cgi_name)-1] = 0; 172 | cgi->tmpl->put(cgi->tmpl, cgi_name, var->value, NULL); 173 | } 174 | 175 | 176 | /* 177 | parse a url encoded form 178 | */ 179 | static void load_urlencoded(struct cgi_state *cgi) 180 | { 181 | unsigned len = cgi->content_length; 182 | char *line; 183 | char *p; 184 | 185 | while (len && (line=grab_line(cgi, "&", &len))) { 186 | p = strchr(line,'='); 187 | if (p) { 188 | *p = 0; 189 | put(cgi, line, p+1); 190 | } 191 | talloc_free(line); 192 | } 193 | } 194 | 195 | /* 196 | parse a single element of a multipart encoded form 197 | It's rather more complex than I would like :( 198 | */ 199 | static int load_one_part(struct cgi_state *cgi, unsigned *len, char *boundary) 200 | { 201 | char *line; 202 | char *name=NULL; 203 | char *content; 204 | char *filename=NULL; 205 | unsigned content_len=0, content_alloc=1024; 206 | unsigned boundary_len = strlen(boundary); 207 | int raw_data = 0; 208 | 209 | while (*len && (line=grab_line(cgi, CRLF, len))) { 210 | if (*line == 0) break; 211 | if (strcmp(line,"--") == 0) return 1; 212 | if (strncasecmp(line, CONTENT_TYPE, 213 | strlen(CONTENT_TYPE)) == 0) { 214 | raw_data = 1; 215 | } 216 | if (strncasecmp(line, CONTENT_DISPOSITION, 217 | strlen(CONTENT_DISPOSITION)) == 0) { 218 | char *p = strstr(line,"; name="); 219 | if (!p) continue; 220 | p += 7; 221 | if (*p == '"') p++; 222 | name = talloc_strndup(cgi, p, strcspn(p, "\";")); 223 | p = strstr(line,"; filename=\""); 224 | if (p) { 225 | p += 12; 226 | filename = talloc_strndup(cgi, p, strcspn(p, "\";")); 227 | } 228 | } 229 | } 230 | 231 | content = talloc_size(cgi, content_alloc); 232 | 233 | char c; 234 | while (*len && cgi_read(cgi, &c, 1) == 1) { 235 | (*len)--; 236 | if (content_len >= (content_alloc-1)) { 237 | // if bigger than 1k, then allocate enough for whole object 238 | if (content_alloc < cgi->content_length) { 239 | content_alloc = cgi->content_length; 240 | } else { 241 | content_alloc *= 2; 242 | } 243 | content = talloc_realloc_size(cgi, content, content_alloc); 244 | if (!content) { 245 | return 1; 246 | } 247 | } 248 | content[content_len++] = c; 249 | /* we keep grabbing content until we hit a boundary */ 250 | if (content_len >= boundary_len && 251 | memcmp(boundary, &content[content_len-boundary_len], 252 | boundary_len) == 0 && 253 | memcmp("--", &content[content_len-boundary_len-2], 2) == 0) { 254 | content_len -= boundary_len+4; 255 | if (name) { 256 | if (raw_data || filename) { 257 | put(cgi, name, filename?filename:""); 258 | content = talloc_realloc_size(cgi, content, content_len); 259 | if (!content) { 260 | return 1; 261 | } 262 | cgi->variables->content = content; 263 | cgi->variables->content_len = content_len; 264 | } else { 265 | content[content_len] = 0; 266 | put(cgi, name, content); 267 | talloc_free(name); 268 | talloc_free(content); 269 | } 270 | } else { 271 | talloc_free(content); 272 | } 273 | char b[2]; 274 | cgi_read(cgi, b, 2); 275 | (*len) -= 2; 276 | return 0; 277 | } 278 | } 279 | 280 | if (filename) { 281 | talloc_free(filename); 282 | } 283 | 284 | return 1; 285 | } 286 | 287 | /* 288 | parse a multipart encoded form (for file upload) 289 | see rfc1867 290 | */ 291 | static void load_multipart(struct cgi_state *cgi) 292 | { 293 | char *boundary; 294 | unsigned len = cgi->content_length; 295 | char *line; 296 | 297 | if (!cgi->content_type) return; 298 | boundary = strstr(cgi->content_type, "boundary="); 299 | if (!boundary) return; 300 | boundary += 9; 301 | trim_tail(boundary, CRLF); 302 | line = grab_line(cgi, CRLF, &len); 303 | if (strncmp(line,"--", 2) != 0 || 304 | strncmp(line+2,boundary,strlen(boundary)) != 0) { 305 | console_printf("Malformed multipart?\n"); 306 | talloc_free(line); 307 | return; 308 | } 309 | 310 | if (strcmp(line+2+strlen(boundary), "--") == 0) { 311 | /* the end is only the beginning ... */ 312 | talloc_free(line); 313 | return; 314 | } 315 | 316 | talloc_free(line); 317 | while (load_one_part(cgi, &len, boundary) == 0) ; 318 | } 319 | 320 | /* 321 | load all the variables passed to the CGI program. May have multiple variables 322 | with the same name and the same or different values. 323 | */ 324 | static void load_variables(struct cgi_state *cgi) 325 | { 326 | char *p, *s, *tok; 327 | 328 | if (cgi->content_length > 0 && cgi->request_post) { 329 | if (strncmp(cgi->content_type, MULTIPART_FORM_DATA, 330 | strlen(MULTIPART_FORM_DATA)) == 0) { 331 | load_multipart(cgi); 332 | } else { 333 | load_urlencoded(cgi); 334 | } 335 | } 336 | 337 | if ((s=cgi->query_string)) { 338 | char *pp; 339 | for (tok=strtok_r(s,"&;", &pp);tok;tok=strtok_r(NULL,"&;", &pp)) { 340 | p = strchr(tok,'='); 341 | if (p) { 342 | *p = 0; 343 | put(cgi, tok, p+1); 344 | } 345 | } 346 | } 347 | } 348 | 349 | 350 | /* 351 | find a variable passed via CGI 352 | Doesn't quite do what you think in the case of POST text variables, because 353 | if they exist they might have a value of "" or even " ", depending on the 354 | browser. Also doesn't allow for variables[] containing multiple variables 355 | with the same name and the same or different values. 356 | */ 357 | static const char *get(struct cgi_state *cgi, const char *name) 358 | { 359 | struct cgi_var *var; 360 | 361 | for (var = cgi->variables; var; var = var->next) { 362 | //console_printf("var: %s=%s\n", var->name, var->value); 363 | if (strcmp(var->name, name) == 0) { 364 | return var->value; 365 | } 366 | } 367 | return NULL; 368 | } 369 | 370 | /* 371 | return the content of a binary cgi variable (for file upload) 372 | */ 373 | static const char *get_content(struct cgi_state *cgi, const char *name, unsigned *size) 374 | { 375 | struct cgi_var *var; 376 | 377 | for (var = cgi->variables; var; var = var->next) { 378 | if (strcmp(var->name, name) == 0) { 379 | *size = var->content_len; 380 | return var->content; 381 | } 382 | } 383 | return NULL; 384 | } 385 | 386 | /* 387 | get a line from cgi socket 388 | */ 389 | static bool cgi_gets(struct cgi_state *cgi, char *line, uint32_t size) 390 | { 391 | uint32_t n = 0; 392 | while (n < size-1) { 393 | char c; 394 | if (cgi_read(cgi, &c, 1) != 1) { 395 | break; 396 | } 397 | if (c == '\n') { 398 | break; 399 | } 400 | line[n++] = c; 401 | } 402 | line[n] = 0; 403 | return n > 0; 404 | } 405 | 406 | /* 407 | tell a browser about a fatal error in the http processing 408 | */ 409 | static void http_error(struct cgi_state *cgi, 410 | const char *err, const char *header, const char *info) 411 | { 412 | if (!cgi->got_request) { 413 | /* damn browsers don't like getting cut off before they give a request */ 414 | char line[1024]; 415 | while (cgi_gets(cgi, line, sizeof(line))) { { 416 | if (strncasecmp(line,"GET ", 4)==0 || 417 | strncasecmp(line,"POST ", 5)==0 || 418 | strncasecmp(line,"PUT ", 4)==0) { 419 | break; 420 | } 421 | } 422 | } 423 | } 424 | sock_printf(cgi->sock, "HTTP/1.0 %s\r\n%sConnection: close\r\nContent-Type: text/html\r\n\r\n%s

%s

%s

\r\n\r\n", err, header, err, err, info); 425 | } 426 | 427 | static const struct mime_type { 428 | const char *pattern; 429 | const char *mime_type; 430 | enum CGI_MIME_TYPE type; 431 | } mime_types[] = { 432 | {".gif", "image/gif", MIME_TYPE_IMAGE_GIF}, 433 | {".jpg", "image/jpeg", MIME_TYPE_IMAGE_JPEG}, 434 | {".txt", "text/plain", MIME_TYPE_TEXT_PLAIN}, 435 | {".html", "text/html;charset=UTF-8", MIME_TYPE_TEXT_HTML}, 436 | {".mp4", "video/mp4", MIME_TYPE_VIDEO_MP4}, 437 | {".avi", "video/avi", MIME_TYPE_VIDEO_AVI}, 438 | {".bin", "data", MIME_TYPE_UNKNOWN}, 439 | {".svg", "image/svg+xml", MIME_TYPE_IMAGE_SVG}, 440 | {".js", "application/javascript", MIME_TYPE_JAVASCRIPT}, 441 | {".json", "application/json", MIME_TYPE_JSON}, 442 | {".css", "text/css", MIME_TYPE_CSS}, 443 | {".mjpg", "multipart/x-mixed-replace; boundary=mjpgboundary", MIME_TYPE_MJPG}, 444 | {NULL, "data", MIME_TYPE_UNKNOWN}, 445 | }; 446 | 447 | /* 448 | send a http header based on file extension 449 | */ 450 | static const struct mime_type *get_mime_type(const char *filename) 451 | { 452 | int i; 453 | 454 | int len = strlen(filename); 455 | for (i=0; mime_types[i].pattern != NULL; i++) { 456 | int plen = strlen(mime_types[i].pattern); 457 | if (len >= plen && strcasecmp(&filename[len-plen], mime_types[i].pattern) == 0) { 458 | break; 459 | } 460 | } 461 | return &mime_types[i]; 462 | } 463 | 464 | /* 465 | send a http header based on file extension 466 | */ 467 | static void http_header(struct cgi_state *cgi, const char *filename) 468 | { 469 | const struct mime_type *mtype = get_mime_type(filename); 470 | 471 | sock_printf(cgi->sock, "HTTP/1.0 200 OK\r\nConnection: close\r\n"); 472 | 473 | sock_printf(cgi->sock, "Content-Type: %s\r\n", mtype->mime_type); 474 | if (cgi->content_length > 0) { 475 | sock_printf(cgi->sock, "Content-Length: %u\r\n", 476 | (unsigned)cgi->content_length); 477 | } 478 | sock_printf(cgi->sock, "Access-Control-Allow-Origin: *\r\n"); 479 | if (mtype->type != MIME_TYPE_TEXT_HTML && 480 | mtype->type != MIME_TYPE_JSON && 481 | strncmp(filename, "ajax/", 5) != 0 && 482 | strncmp(filename, "fs/", 3) != 0) { 483 | //console_printf("serving %s\n", filename); 484 | sock_printf(cgi->sock, "Cache-Control: public, max-age=3600\r\n"); 485 | } 486 | if (cgi->sock->add_content_length) { 487 | // delay the content length header 488 | cgi->sock->header_length = talloc_get_size(cgi->sock->buf); 489 | } else { 490 | sock_printf(cgi->sock, "\r\n"); 491 | } 492 | } 493 | 494 | #ifdef SYSTEM_FREERTOS 495 | /* 496 | handle FAT filesystem list request 497 | */ 498 | static void download_file_list(struct cgi_state *cgi, const char *path) 499 | { 500 | FRESULT ret; 501 | DIR dir; 502 | char *path2 = talloc_strdup(cgi, path); 503 | if (!path2) { 504 | cgi->http_error(cgi, "404 Bad File", "", "file not found"); 505 | return; 506 | } 507 | if (path2[strlen(path2)-1] == '/') { 508 | path2[strlen(path2)-1] = 0; 509 | } 510 | 511 | put(cgi, "FILENAME", path2); 512 | 513 | if ((ret = f_opendir(&dir, path2)) != FR_OK) { 514 | cgi->http_error(cgi, "404 Bad File", "", "file not found"); 515 | talloc_free(path2); 516 | return; 517 | } 518 | 519 | cgi->http_header(cgi, "index.html"); 520 | cgi->tmpl->process(cgi->tmpl, "dirheader.html", 1); 521 | 522 | FILLIST ff = {}; 523 | ff.finfo.lfname = ff.lfname; 524 | ff.finfo.lfsize = sizeof(ff.lfname); 525 | while ((ret = f_readdir(&dir, &ff.finfo))==FR_OK) { 526 | if (strcmp(ff.finfo.fname, ".") == 0) { 527 | continue; 528 | } 529 | if (ff.finfo.fname[0] == 0) { 530 | break; 531 | } 532 | const char *fname = GET_FN(ff.finfo); 533 | sock_printf(cgi->sock, "%s%s%s%04u-%02u-%02u %02u:%02u%u\n", 534 | (ff.finfo.fattrib&AM_DIR)?"d":"-", 535 | fname, 536 | (ff.finfo.fattrib&AM_DIR)?"/":"", 537 | fname, 538 | (ff.finfo.fattrib&AM_DIR)?"/":"", 539 | FF_YEAR(ff.finfo.fdate), FF_MONTH(ff.finfo.fdate), FF_DATE(ff.finfo.fdate), 540 | FF_HOUR(ff.finfo.ftime), FF_MINUTE(ff.finfo.ftime), 541 | (unsigned)ff.finfo.fsize); 542 | } 543 | f_closedir(&dir); 544 | sock_printf(cgi->sock, "\n"); 545 | cgi->tmpl->process(cgi->tmpl, "dirfooter.html", 1); 546 | talloc_free(path2); 547 | } 548 | 549 | /* 550 | handle FAT filesystem request 551 | */ 552 | static void download_filesystem(struct cgi_state *cgi, const char *fs_path) 553 | { 554 | const char *path = fs_path+2; 555 | FILLIST f = {}; 556 | f.finfo.lfname = f.lfname; 557 | f.finfo.lfsize = sizeof(f.lfname); 558 | if (strcmp(path, "/") == 0 || 559 | path[strlen(path)-1] == '/' || 560 | (f_stat(path, &f.finfo) == FR_OK && (f.finfo.fattrib & AM_DIR) != 0)) { 561 | download_file_list(cgi, path); 562 | return; 563 | } 564 | cgi->content_length = f.finfo.fsize; 565 | FIL fh; 566 | if (f_open(&fh, path, FA_READ) == FR_OK) { 567 | cgi->http_header(cgi, fs_path); 568 | char buf[2048]; 569 | UINT read_count; 570 | while (f_read(&fh, buf, sizeof(buf), &read_count) == FR_OK && read_count>0) { 571 | if (sock_write(cgi->sock, buf, read_count) != read_count) { 572 | break; 573 | } 574 | } 575 | f_close(&fh); 576 | } else { 577 | cgi->http_error(cgi, "404 Bad File", "", "file not found"); 578 | } 579 | } 580 | #endif // SYSTEM_FREERTOS 581 | 582 | /* 583 | handle a file download 584 | */ 585 | static void download(struct cgi_state *cgi, const char *path) 586 | { 587 | const struct mime_type *mtype; 588 | 589 | if (!path || *path == 0) { 590 | // handle root page 591 | path = "index.html"; 592 | } 593 | 594 | mtype = get_mime_type(path); 595 | 596 | if (strncmp(path, "fs/", 3) == 0) { 597 | download_filesystem(cgi, path); 598 | return; 599 | } 600 | 601 | size_t size = 0; 602 | const char *contents = get_embedded_file(path, &size); 603 | if (!contents) { 604 | web_debug(2, "not found: %s\n", path); 605 | cgi->http_error(cgi, "404 Bad File", "", "file not found"); 606 | return; 607 | } 608 | 609 | if (mtype->type == MIME_TYPE_TEXT_HTML || 610 | mtype->type == MIME_TYPE_JSON || 611 | strncmp(path, "ajax/", 5) == 0) { 612 | cgi->content_length = 0; 613 | if (mtype->type != MIME_TYPE_MJPG) { 614 | cgi->sock->add_content_length = true; 615 | } 616 | cgi->http_header(cgi, path); 617 | web_debug(2, "process: %s\n", path); 618 | cgi->tmpl->process(cgi->tmpl, path, 1); 619 | return; 620 | } 621 | 622 | web_debug(2, "embedded: %s\n", path); 623 | cgi->content_length = size; 624 | cgi->http_header(cgi, path); 625 | sock_write(cgi->sock, contents, size); 626 | } 627 | 628 | 629 | /* setup headers for standalone web server */ 630 | static bool setup_standalone(struct cgi_state *cgi) 631 | { 632 | char line[1024]; 633 | char *url=NULL; 634 | char *p; 635 | 636 | while (cgi_gets(cgi, line, sizeof(line))) { 637 | trim_tail(line, CRLF); 638 | if (line[0] == 0) break; 639 | if (strncasecmp(line,"GET ", 4)==0) { 640 | cgi->got_request = 1; 641 | url = talloc_strdup(cgi, &line[4]); 642 | } else if (strncasecmp(line,"POST ", 5)==0) { 643 | cgi->got_request = 1; 644 | cgi->request_post = 1; 645 | url = talloc_strdup(cgi, &line[5]); 646 | } else if (strncasecmp(line,"PUT ", 4)==0) { 647 | cgi->got_request = 1; 648 | cgi->http_error(cgi, "400 Bad Request", "", 649 | "This server does not accept PUT requests"); 650 | return false; 651 | } else if (strncasecmp(line,"Content-Length: ", 16)==0) { 652 | cgi->content_length = atoi(&line[16]); 653 | } else if (strncasecmp(line,"Content-Type: ", 14)==0) { 654 | cgi->content_type = talloc_strdup(cgi, &line[14]); 655 | } else if (strncasecmp(line,"Origin: ", 8)==0) { 656 | cgi->origin = talloc_strdup(cgi, &line[8]); 657 | if (cgi->check_origin != NULL && !cgi->check_origin(cgi->origin)) { 658 | cgi->http_error(cgi, "400 Bad Origin", "", 659 | "request with incorrect origin header"); 660 | } 661 | } 662 | /* ignore all other requests! */ 663 | } 664 | 665 | if (!url) { 666 | cgi->http_error(cgi, "400 Bad Request", "", 667 | "You must specify a GET or POST request"); 668 | return false; 669 | } 670 | 671 | /* trim the URL */ 672 | if ((p = strchr(url,' ')) || (p=strchr(url,'\t'))) { 673 | *p = 0; 674 | } 675 | 676 | /* anything following a ? in the URL is part of the query string */ 677 | if ((p=strchr(url,'?'))) { 678 | cgi->query_string = p+1; 679 | *p = 0; 680 | } 681 | 682 | cgi->url = url; 683 | cgi->pathinfo = url; 684 | return true; 685 | } 686 | 687 | /* 688 | read and parse the http request 689 | */ 690 | static bool setup(struct cgi_state *cgi) 691 | { 692 | bool ret; 693 | ret = setup_standalone(cgi); 694 | 695 | while (cgi->pathinfo && *cgi->pathinfo == '/') cgi->pathinfo++; 696 | 697 | return ret; 698 | } 699 | 700 | static struct cgi_state cgi_base = { 701 | /* methods */ 702 | setup, 703 | http_header, 704 | load_variables, 705 | get, 706 | get_content, 707 | http_error, 708 | download, 709 | put, 710 | 711 | /* rest are zero */ 712 | }; 713 | 714 | struct cgi_state *cgi_init(struct connection_state *c, struct sock_buf *sock) 715 | { 716 | struct cgi_state *cgi; 717 | 718 | cgi = talloc_zero(c, struct cgi_state); 719 | if (cgi == NULL) { 720 | return NULL; 721 | } 722 | memcpy(cgi, &cgi_base, sizeof(*cgi)); 723 | 724 | cgi->tmpl = template_init(cgi, sock); 725 | cgi->sock = sock; 726 | 727 | return cgi; 728 | } 729 | 730 | -------------------------------------------------------------------------------- /cgi.h: -------------------------------------------------------------------------------- 1 | /* 2 | some simple CGI helper routines 3 | Copyright (C) Andrew Tridgell 1997-2002 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include "web_server.h" 24 | 25 | struct cgi_var { 26 | struct cgi_var *next; 27 | char *name; 28 | char *value; 29 | char *content; 30 | unsigned content_len; 31 | }; 32 | 33 | struct cgi_state { 34 | /* methods */ 35 | bool (*setup)(struct cgi_state *); 36 | void (*http_header)(struct cgi_state *, const char *); 37 | void (*load_variables)(struct cgi_state *); 38 | const char *(*get)(struct cgi_state *, const char *); 39 | const char *(*get_content)(struct cgi_state *, const char *, unsigned *size); 40 | void (*http_error)(struct cgi_state *cgi, 41 | const char *err, const char *header, const char *info); 42 | void (*download)(struct cgi_state *cgi, const char *path); 43 | void (*put)(struct cgi_state *cgi, const char *name, const char *value); 44 | bool (*check_origin)(const char *origin); 45 | 46 | /* data */ 47 | struct cgi_var *variables; 48 | struct template_state *tmpl; 49 | const char *origin; 50 | char *content_type; 51 | unsigned long content_length; 52 | int request_post; 53 | char *query_string; 54 | char *pathinfo; 55 | char *url; 56 | int got_request; 57 | struct sock_buf *sock; 58 | char buf[512]; 59 | uint16_t buflen; 60 | uint16_t bufofs; 61 | }; 62 | 63 | 64 | enum CGI_MIME_TYPE {MIME_TYPE_IMAGE_GIF, 65 | MIME_TYPE_IMAGE_JPEG, 66 | MIME_TYPE_IMAGE_SVG, 67 | MIME_TYPE_TEXT_PLAIN, 68 | MIME_TYPE_TEXT_HTML, 69 | MIME_TYPE_VIDEO_MP4, 70 | MIME_TYPE_VIDEO_AVI, 71 | MIME_TYPE_JAVASCRIPT, 72 | MIME_TYPE_JSON, 73 | MIME_TYPE_CSS, 74 | MIME_TYPE_MJPG, 75 | MIME_TYPE_UNKNOWN}; 76 | 77 | /* prototypes */ 78 | struct cgi_state *cgi_init(struct connection_state *c, struct sock_buf *sock); 79 | void trim_tail(char *s, char *trim_chars); 80 | 81 | -------------------------------------------------------------------------------- /files/.gitignore: -------------------------------------------------------------------------------- 1 | /embedded.c 2 | /manifest.appcache 3 | /version.h 4 | -------------------------------------------------------------------------------- /files/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | FileETag None 3 | 4 | Header unset ETag 5 | Header set Cache-Control "max-age=0, no-cache, must-revalidate" 6 | Header set Pragma "no-cache" 7 | Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT" 8 | Header set Access-Control-Allow-Origin "*" 9 | 10 | 11 | 12 | FileETag None 13 | 14 | Header set Access-Control-Allow-Origin "*" 15 | 16 | 17 | -------------------------------------------------------------------------------- /files/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @echo "Generating embedded.c" 3 | @./embed.py *.html images/*.svg */*.js */*.json */*.css */*.jpg */*.png */*.mjpg data/*.xml 4 | @echo "Generating manifest" 5 | @./gen_manifest.sh 6 | @echo "Generating version.h" 7 | @./gen_version.sh 8 | 9 | -------------------------------------------------------------------------------- /files/README.md: -------------------------------------------------------------------------------- 1 | With thanks for the following included modules: 2 | 3 | http://leafletjs.com/ 4 | https://github.com/gmaclennan/leaflet-bing-layer 5 | http://smoothiecharts.org/ 6 | https://github.com/bbecquet/Leaflet.RotatedMarker 7 | -------------------------------------------------------------------------------- /files/ajax/command.json: -------------------------------------------------------------------------------- 1 | {{@process_c_calls()}} 2 | -------------------------------------------------------------------------------- /files/ajax/snapshot.jpg: -------------------------------------------------------------------------------- 1 | {{@snapshot()}} 2 | -------------------------------------------------------------------------------- /files/ajax/sysinfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "uptime": {{ @uptime() }}, 3 | "free_mem_dma": {{ @mem_free(1) }}, 4 | "free_mem_kernel": {{ @mem_free(2) }}, 5 | "fc_mavlink_count": {{ @fc_mavlink_count() }}, 6 | "fc_mavlink_baudrate": {{ @fc_mavlink_baudrate() }} 7 | } 8 | -------------------------------------------------------------------------------- /files/ajax/upgrade.json: -------------------------------------------------------------------------------- 1 | { 2 | "upload_progress": {{ @upload_progress() }}, 3 | "upload_message": "{{ @upload_message() }}" 4 | } 5 | -------------------------------------------------------------------------------- /files/ajax/version.json: -------------------------------------------------------------------------------- 1 | {{@sonix_version()}} 2 | -------------------------------------------------------------------------------- /files/ajax/video.mjpg: -------------------------------------------------------------------------------- 1 | {{@mjpgvideo()}} -------------------------------------------------------------------------------- /files/calibration.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArduPilot 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |

ArduPilot

19 | 20 |
21 | 22 | 23 |
24 | 25 |
26 | 27 |

Accelerometer calibration

28 | 29 | There are two types of accelerometer calibration available. Please 30 | choose which type of calibration you would like. 31 | 32 |
33 |

Simple Accelerometer Calibration

34 | 35 | A simple accelerometer calibration involves you placing your vehicle 36 | on a level surface and pressing the button below. The calibration will 37 | take just a few seconds. This type of calibration is good for most 38 | users. 39 | 40 |

41 | 42 |


43 | 44 |


45 |

Six-Axis Accelerometer Calibration

46 | 47 | A six axis accelerometer calibration involves you placing your vehicle 48 | in each of 6 orientations in turn, as you are guided by the prompts 49 | below. This type of calibration is good if you will be doing 3D 50 | flying, or if you find that you get poor results with a simple 51 | accelerometer calibration. 52 | 53 |

After a six-axis calibration you will need to power cycle your vehicle before you can fly. 54 | 55 |

56 | 57 |
58 |


59 |

60 |


61 | 62 |


63 | 64 |

Sensor State

65 | 66 | 67 | 95 | 99 | 100 |
68 | 69 | 70 | 71 | 72 | 73 |
VariableValue
Roll
Pitch
74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 |
NameXYZ
Sensor
Offsets
Scaling
93 | 94 |
96 | Accelerometers (X, Y, Z)
97 |

98 |

101 |
102 | 103 |
104 | 105 |

Magnetometer calibration

106 | 107 | To calibrate the magnetometer you will need to move to a location well 108 | away from all metal. Then press the button below to start 109 | calibration. Once started you will need to rotate the vehicle about 110 | all axes. 111 | 112 |

A progress bar will display to show how far you are through the calibration. 113 | 114 |

After a magnetometer calibration you will need to power cycle your vehicle before you can fly. 115 | 116 |

117 | 118 |
119 |
120 |

Calibration progress:
121 |

122 |
123 |
124 |
125 |
126 | 127 |


128 | 129 |


130 | 131 |

Sensor State

132 | 133 | 157 | 161 | 162 |
134 | 135 | 136 | 137 | 138 | 139 | 140 |
VariableValue
Roll
Pitch
Yaw
141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 |
SensorXYZ
Magnetometer
Offsets
155 | 156 |
158 | Magnetometer (X, Y, Z)
159 |

160 |

163 |
164 | 165 | 166 |
167 |

home 168 | 169 | 356 | 357 | 358 | 359 | 360 | -------------------------------------------------------------------------------- /files/css/leaflet.css: -------------------------------------------------------------------------------- 1 | /* required styles */ 2 | 3 | .leaflet-pane, 4 | .leaflet-tile, 5 | .leaflet-marker-icon, 6 | .leaflet-marker-shadow, 7 | .leaflet-tile-container, 8 | .leaflet-pane > svg, 9 | .leaflet-pane > canvas, 10 | .leaflet-zoom-box, 11 | .leaflet-image-layer, 12 | .leaflet-layer { 13 | position: absolute; 14 | left: 0; 15 | top: 0; 16 | } 17 | .leaflet-container { 18 | overflow: hidden; 19 | } 20 | .leaflet-tile, 21 | .leaflet-marker-icon, 22 | .leaflet-marker-shadow { 23 | -webkit-user-select: none; 24 | -moz-user-select: none; 25 | user-select: none; 26 | -webkit-user-drag: none; 27 | } 28 | /* Safari renders non-retina tile on retina better with this, but Chrome is worse */ 29 | .leaflet-safari .leaflet-tile { 30 | image-rendering: -webkit-optimize-contrast; 31 | } 32 | /* hack that prevents hw layers "stretching" when loading new tiles */ 33 | .leaflet-safari .leaflet-tile-container { 34 | width: 1600px; 35 | height: 1600px; 36 | -webkit-transform-origin: 0 0; 37 | } 38 | .leaflet-marker-icon, 39 | .leaflet-marker-shadow { 40 | display: block; 41 | } 42 | /* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */ 43 | /* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */ 44 | .leaflet-container .leaflet-overlay-pane svg, 45 | .leaflet-container .leaflet-marker-pane img, 46 | .leaflet-container .leaflet-shadow-pane img, 47 | .leaflet-container .leaflet-tile-pane img, 48 | .leaflet-container img.leaflet-image-layer { 49 | max-width: none !important; 50 | } 51 | 52 | .leaflet-container.leaflet-touch-zoom { 53 | -ms-touch-action: pan-x pan-y; 54 | touch-action: pan-x pan-y; 55 | } 56 | .leaflet-container.leaflet-touch-drag { 57 | -ms-touch-action: pinch-zoom; 58 | } 59 | .leaflet-container.leaflet-touch-drag.leaflet-touch-zoom { 60 | -ms-touch-action: none; 61 | touch-action: none; 62 | } 63 | .leaflet-tile { 64 | filter: inherit; 65 | visibility: hidden; 66 | } 67 | .leaflet-tile-loaded { 68 | visibility: inherit; 69 | } 70 | .leaflet-zoom-box { 71 | width: 0; 72 | height: 0; 73 | -moz-box-sizing: border-box; 74 | box-sizing: border-box; 75 | z-index: 800; 76 | } 77 | /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ 78 | .leaflet-overlay-pane svg { 79 | -moz-user-select: none; 80 | } 81 | 82 | .leaflet-pane { z-index: 400; } 83 | 84 | .leaflet-tile-pane { z-index: 200; } 85 | .leaflet-overlay-pane { z-index: 400; } 86 | .leaflet-shadow-pane { z-index: 500; } 87 | .leaflet-marker-pane { z-index: 600; } 88 | .leaflet-tooltip-pane { z-index: 650; } 89 | .leaflet-popup-pane { z-index: 700; } 90 | 91 | .leaflet-map-pane canvas { z-index: 100; } 92 | .leaflet-map-pane svg { z-index: 200; } 93 | 94 | .leaflet-vml-shape { 95 | width: 1px; 96 | height: 1px; 97 | } 98 | .lvml { 99 | behavior: url(#default#VML); 100 | display: inline-block; 101 | position: absolute; 102 | } 103 | 104 | 105 | /* control positioning */ 106 | 107 | .leaflet-control { 108 | position: relative; 109 | z-index: 800; 110 | pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ 111 | pointer-events: auto; 112 | } 113 | .leaflet-top, 114 | .leaflet-bottom { 115 | position: absolute; 116 | z-index: 1000; 117 | pointer-events: none; 118 | } 119 | .leaflet-top { 120 | top: 0; 121 | } 122 | .leaflet-right { 123 | right: 0; 124 | } 125 | .leaflet-bottom { 126 | bottom: 0; 127 | } 128 | .leaflet-left { 129 | left: 0; 130 | } 131 | .leaflet-control { 132 | float: left; 133 | clear: both; 134 | } 135 | .leaflet-right .leaflet-control { 136 | float: right; 137 | } 138 | .leaflet-top .leaflet-control { 139 | margin-top: 10px; 140 | } 141 | .leaflet-bottom .leaflet-control { 142 | margin-bottom: 10px; 143 | } 144 | .leaflet-left .leaflet-control { 145 | margin-left: 10px; 146 | } 147 | .leaflet-right .leaflet-control { 148 | margin-right: 10px; 149 | } 150 | 151 | 152 | /* zoom and fade animations */ 153 | 154 | .leaflet-fade-anim .leaflet-tile { 155 | will-change: opacity; 156 | } 157 | .leaflet-fade-anim .leaflet-popup { 158 | opacity: 0; 159 | -webkit-transition: opacity 0.2s linear; 160 | -moz-transition: opacity 0.2s linear; 161 | -o-transition: opacity 0.2s linear; 162 | transition: opacity 0.2s linear; 163 | } 164 | .leaflet-fade-anim .leaflet-map-pane .leaflet-popup { 165 | opacity: 1; 166 | } 167 | .leaflet-zoom-animated { 168 | -webkit-transform-origin: 0 0; 169 | -ms-transform-origin: 0 0; 170 | transform-origin: 0 0; 171 | } 172 | .leaflet-zoom-anim .leaflet-zoom-animated { 173 | will-change: transform; 174 | } 175 | .leaflet-zoom-anim .leaflet-zoom-animated { 176 | -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); 177 | -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); 178 | -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); 179 | transition: transform 0.25s cubic-bezier(0,0,0.25,1); 180 | } 181 | .leaflet-zoom-anim .leaflet-tile, 182 | .leaflet-pan-anim .leaflet-tile { 183 | -webkit-transition: none; 184 | -moz-transition: none; 185 | -o-transition: none; 186 | transition: none; 187 | } 188 | 189 | .leaflet-zoom-anim .leaflet-zoom-hide { 190 | visibility: hidden; 191 | } 192 | 193 | 194 | /* cursors */ 195 | 196 | .leaflet-interactive { 197 | cursor: pointer; 198 | } 199 | .leaflet-grab { 200 | cursor: -webkit-grab; 201 | cursor: -moz-grab; 202 | } 203 | .leaflet-crosshair, 204 | .leaflet-crosshair .leaflet-interactive { 205 | cursor: crosshair; 206 | } 207 | .leaflet-popup-pane, 208 | .leaflet-control { 209 | cursor: auto; 210 | } 211 | .leaflet-dragging .leaflet-grab, 212 | .leaflet-dragging .leaflet-grab .leaflet-interactive, 213 | .leaflet-dragging .leaflet-marker-draggable { 214 | cursor: move; 215 | cursor: -webkit-grabbing; 216 | cursor: -moz-grabbing; 217 | } 218 | 219 | /* marker & overlays interactivity */ 220 | .leaflet-marker-icon, 221 | .leaflet-marker-shadow, 222 | .leaflet-image-layer, 223 | .leaflet-pane > svg path, 224 | .leaflet-tile-container { 225 | pointer-events: none; 226 | } 227 | 228 | .leaflet-marker-icon.leaflet-interactive, 229 | .leaflet-image-layer.leaflet-interactive, 230 | .leaflet-pane > svg path.leaflet-interactive { 231 | pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */ 232 | pointer-events: auto; 233 | } 234 | 235 | /* visual tweaks */ 236 | 237 | .leaflet-container { 238 | background: #ddd; 239 | outline: 0; 240 | } 241 | .leaflet-container a { 242 | color: #0078A8; 243 | } 244 | .leaflet-container a.leaflet-active { 245 | outline: 2px solid orange; 246 | } 247 | .leaflet-zoom-box { 248 | border: 2px dotted #38f; 249 | background: rgba(255,255,255,0.5); 250 | } 251 | 252 | 253 | /* general typography */ 254 | .leaflet-container { 255 | font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; 256 | } 257 | 258 | 259 | /* general toolbar styles */ 260 | 261 | .leaflet-bar { 262 | box-shadow: 0 1px 5px rgba(0,0,0,0.65); 263 | border-radius: 4px; 264 | } 265 | .leaflet-bar a, 266 | .leaflet-bar a:hover { 267 | background-color: #fff; 268 | border-bottom: 1px solid #ccc; 269 | width: 26px; 270 | height: 26px; 271 | line-height: 26px; 272 | display: block; 273 | text-align: center; 274 | text-decoration: none; 275 | color: black; 276 | } 277 | .leaflet-bar a, 278 | .leaflet-control-layers-toggle { 279 | background-position: 50% 50%; 280 | background-repeat: no-repeat; 281 | display: block; 282 | } 283 | .leaflet-bar a:hover { 284 | background-color: #f4f4f4; 285 | } 286 | .leaflet-bar a:first-child { 287 | border-top-left-radius: 4px; 288 | border-top-right-radius: 4px; 289 | } 290 | .leaflet-bar a:last-child { 291 | border-bottom-left-radius: 4px; 292 | border-bottom-right-radius: 4px; 293 | border-bottom: none; 294 | } 295 | .leaflet-bar a.leaflet-disabled { 296 | cursor: default; 297 | background-color: #f4f4f4; 298 | color: #bbb; 299 | } 300 | 301 | .leaflet-touch .leaflet-bar a { 302 | width: 30px; 303 | height: 30px; 304 | line-height: 30px; 305 | } 306 | 307 | 308 | /* zoom control */ 309 | 310 | .leaflet-control-zoom-in, 311 | .leaflet-control-zoom-out { 312 | font: bold 18px 'Lucida Console', Monaco, monospace; 313 | text-indent: 1px; 314 | } 315 | .leaflet-control-zoom-out { 316 | font-size: 20px; 317 | } 318 | 319 | .leaflet-touch .leaflet-control-zoom-in { 320 | font-size: 22px; 321 | } 322 | .leaflet-touch .leaflet-control-zoom-out { 323 | font-size: 24px; 324 | } 325 | 326 | 327 | /* layers control */ 328 | 329 | .leaflet-control-layers { 330 | box-shadow: 0 1px 5px rgba(0,0,0,0.4); 331 | background: #fff; 332 | border-radius: 5px; 333 | } 334 | .leaflet-control-layers-toggle { 335 | background-image: url(images/layers.png); 336 | width: 36px; 337 | height: 36px; 338 | } 339 | .leaflet-retina .leaflet-control-layers-toggle { 340 | background-image: url(images/layers-2x.png); 341 | background-size: 26px 26px; 342 | } 343 | .leaflet-touch .leaflet-control-layers-toggle { 344 | width: 44px; 345 | height: 44px; 346 | } 347 | .leaflet-control-layers .leaflet-control-layers-list, 348 | .leaflet-control-layers-expanded .leaflet-control-layers-toggle { 349 | display: none; 350 | } 351 | .leaflet-control-layers-expanded .leaflet-control-layers-list { 352 | display: block; 353 | position: relative; 354 | } 355 | .leaflet-control-layers-expanded { 356 | padding: 6px 10px 6px 6px; 357 | color: #333; 358 | background: #fff; 359 | } 360 | .leaflet-control-layers-scrollbar { 361 | overflow-y: scroll; 362 | padding-right: 5px; 363 | } 364 | .leaflet-control-layers-selector { 365 | margin-top: 2px; 366 | position: relative; 367 | top: 1px; 368 | } 369 | .leaflet-control-layers label { 370 | display: block; 371 | } 372 | .leaflet-control-layers-separator { 373 | height: 0; 374 | border-top: 1px solid #ddd; 375 | margin: 5px -10px 5px -6px; 376 | } 377 | 378 | /* Default icon URLs */ 379 | .leaflet-default-icon-path { 380 | background-image: url(images/marker-icon.png); 381 | } 382 | 383 | 384 | /* attribution and scale controls */ 385 | 386 | .leaflet-container .leaflet-control-attribution { 387 | background: #fff; 388 | background: rgba(255, 255, 255, 0.7); 389 | margin: 0; 390 | } 391 | .leaflet-control-attribution, 392 | .leaflet-control-scale-line { 393 | padding: 0 5px; 394 | color: #333; 395 | } 396 | .leaflet-control-attribution a { 397 | text-decoration: none; 398 | } 399 | .leaflet-control-attribution a:hover { 400 | text-decoration: underline; 401 | } 402 | .leaflet-container .leaflet-control-attribution, 403 | .leaflet-container .leaflet-control-scale { 404 | font-size: 11px; 405 | } 406 | .leaflet-left .leaflet-control-scale { 407 | margin-left: 5px; 408 | } 409 | .leaflet-bottom .leaflet-control-scale { 410 | margin-bottom: 5px; 411 | } 412 | .leaflet-control-scale-line { 413 | border: 2px solid #777; 414 | border-top: none; 415 | line-height: 1.1; 416 | padding: 2px 5px 1px; 417 | font-size: 11px; 418 | white-space: nowrap; 419 | overflow: hidden; 420 | -moz-box-sizing: border-box; 421 | box-sizing: border-box; 422 | 423 | background: #fff; 424 | background: rgba(255, 255, 255, 0.5); 425 | } 426 | .leaflet-control-scale-line:not(:first-child) { 427 | border-top: 2px solid #777; 428 | border-bottom: none; 429 | margin-top: -2px; 430 | } 431 | .leaflet-control-scale-line:not(:first-child):not(:last-child) { 432 | border-bottom: 2px solid #777; 433 | } 434 | 435 | .leaflet-touch .leaflet-control-attribution, 436 | .leaflet-touch .leaflet-control-layers, 437 | .leaflet-touch .leaflet-bar { 438 | box-shadow: none; 439 | } 440 | .leaflet-touch .leaflet-control-layers, 441 | .leaflet-touch .leaflet-bar { 442 | border: 2px solid rgba(0,0,0,0.2); 443 | background-clip: padding-box; 444 | } 445 | 446 | 447 | /* popup */ 448 | 449 | .leaflet-popup { 450 | position: absolute; 451 | text-align: center; 452 | margin-bottom: 20px; 453 | } 454 | .leaflet-popup-content-wrapper { 455 | padding: 1px; 456 | text-align: left; 457 | border-radius: 12px; 458 | } 459 | .leaflet-popup-content { 460 | margin: 13px 19px; 461 | line-height: 1.4; 462 | } 463 | .leaflet-popup-content p { 464 | margin: 18px 0; 465 | } 466 | .leaflet-popup-tip-container { 467 | width: 40px; 468 | height: 20px; 469 | position: absolute; 470 | left: 50%; 471 | margin-left: -20px; 472 | overflow: hidden; 473 | pointer-events: none; 474 | } 475 | .leaflet-popup-tip { 476 | width: 17px; 477 | height: 17px; 478 | padding: 1px; 479 | 480 | margin: -10px auto 0; 481 | 482 | -webkit-transform: rotate(45deg); 483 | -moz-transform: rotate(45deg); 484 | -ms-transform: rotate(45deg); 485 | -o-transform: rotate(45deg); 486 | transform: rotate(45deg); 487 | } 488 | .leaflet-popup-content-wrapper, 489 | .leaflet-popup-tip { 490 | background: white; 491 | color: #333; 492 | box-shadow: 0 3px 14px rgba(0,0,0,0.4); 493 | } 494 | .leaflet-container a.leaflet-popup-close-button { 495 | position: absolute; 496 | top: 0; 497 | right: 0; 498 | padding: 4px 4px 0 0; 499 | border: none; 500 | text-align: center; 501 | width: 18px; 502 | height: 14px; 503 | font: 16px/14px Tahoma, Verdana, sans-serif; 504 | color: #c3c3c3; 505 | text-decoration: none; 506 | font-weight: bold; 507 | background: transparent; 508 | } 509 | .leaflet-container a.leaflet-popup-close-button:hover { 510 | color: #999; 511 | } 512 | .leaflet-popup-scrolled { 513 | overflow: auto; 514 | border-bottom: 1px solid #ddd; 515 | border-top: 1px solid #ddd; 516 | } 517 | 518 | .leaflet-oldie .leaflet-popup-content-wrapper { 519 | zoom: 1; 520 | } 521 | .leaflet-oldie .leaflet-popup-tip { 522 | width: 24px; 523 | margin: 0 auto; 524 | 525 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; 526 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); 527 | } 528 | .leaflet-oldie .leaflet-popup-tip-container { 529 | margin-top: -1px; 530 | } 531 | 532 | .leaflet-oldie .leaflet-control-zoom, 533 | .leaflet-oldie .leaflet-control-layers, 534 | .leaflet-oldie .leaflet-popup-content-wrapper, 535 | .leaflet-oldie .leaflet-popup-tip { 536 | border: 1px solid #999; 537 | } 538 | 539 | 540 | /* div icon */ 541 | 542 | .leaflet-div-icon { 543 | background: #fff; 544 | border: 1px solid #666; 545 | } 546 | 547 | 548 | /* Tooltip */ 549 | /* Base styles for the element that has a tooltip */ 550 | .leaflet-tooltip { 551 | position: absolute; 552 | padding: 6px; 553 | background-color: #fff; 554 | border: 1px solid #fff; 555 | border-radius: 3px; 556 | color: #222; 557 | white-space: nowrap; 558 | -webkit-user-select: none; 559 | -moz-user-select: none; 560 | -ms-user-select: none; 561 | user-select: none; 562 | pointer-events: none; 563 | box-shadow: 0 1px 3px rgba(0,0,0,0.4); 564 | } 565 | .leaflet-tooltip.leaflet-clickable { 566 | cursor: pointer; 567 | pointer-events: auto; 568 | } 569 | .leaflet-tooltip-top:before, 570 | .leaflet-tooltip-bottom:before, 571 | .leaflet-tooltip-left:before, 572 | .leaflet-tooltip-right:before { 573 | position: absolute; 574 | pointer-events: none; 575 | border: 6px solid transparent; 576 | background: transparent; 577 | content: ""; 578 | } 579 | 580 | /* Directions */ 581 | 582 | .leaflet-tooltip-bottom { 583 | margin-top: 6px; 584 | } 585 | .leaflet-tooltip-top { 586 | margin-top: -6px; 587 | } 588 | .leaflet-tooltip-bottom:before, 589 | .leaflet-tooltip-top:before { 590 | left: 50%; 591 | margin-left: -6px; 592 | } 593 | .leaflet-tooltip-top:before { 594 | bottom: 0; 595 | margin-bottom: -12px; 596 | border-top-color: #fff; 597 | } 598 | .leaflet-tooltip-bottom:before { 599 | top: 0; 600 | margin-top: -12px; 601 | margin-left: -6px; 602 | border-bottom-color: #fff; 603 | } 604 | .leaflet-tooltip-left { 605 | margin-left: -6px; 606 | } 607 | .leaflet-tooltip-right { 608 | margin-left: 6px; 609 | } 610 | .leaflet-tooltip-left:before, 611 | .leaflet-tooltip-right:before { 612 | top: 50%; 613 | margin-top: -6px; 614 | } 615 | .leaflet-tooltip-left:before { 616 | right: 0; 617 | margin-right: -12px; 618 | border-left-color: #fff; 619 | } 620 | .leaflet-tooltip-right:before { 621 | left: 0; 622 | margin-left: -12px; 623 | border-right-color: #fff; 624 | } 625 | -------------------------------------------------------------------------------- /files/css/styles.css: -------------------------------------------------------------------------------- 1 | /* border on values tables */ 2 | .values table, .values td, .values th { 3 | border: 1px solid gray 4 | } 5 | 6 | /* tab style */ 7 | div.tab { 8 | overflow: hidden; 9 | border: 1px solid #ccc; 10 | background-color: #f1f1f1; 11 | } 12 | 13 | /* buttons in tabs */ 14 | div.tab button { 15 | background-color: inherit; 16 | float: left; 17 | border: none; 18 | outline: none; 19 | cursor: pointer; 20 | padding: 14px 16px; 21 | transition: 0.3s; 22 | } 23 | 24 | /* highlight on hover */ 25 | div.tab button:hover { 26 | background-color: #ddd; 27 | } 28 | 29 | /* active tab */ 30 | div.tab button.active { 31 | background-color: #ccc; 32 | } 33 | 34 | /* tab content */ 35 | .tabcontent { 36 | display: none; 37 | padding: 6px 12px; 38 | border: 1px solid #ccc; 39 | border-top: none; 40 | } 41 | 42 | #ProgressBlock { 43 | width: 100%; 44 | background-color: #eee; 45 | } 46 | 47 | #ProgressBar { 48 | width: 1%; 49 | height: 30px; 50 | background-color: #4CAF50; 51 | } 52 | 53 | img { 54 | max-width: 100%; 55 | height: auto; 56 | } 57 | 58 | /* map telemetry */ 59 | .telemetry table { 60 | bgcolor: #FFFFFF; 61 | } 62 | 63 | /* parameter table */ 64 | .parameters table, .parameters td, .parameters th { 65 | border: 1px solid gray 66 | } 67 | 68 | .paramvalue { 69 | text-align: right; 70 | } 71 | -------------------------------------------------------------------------------- /files/embed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | ''' 4 | script to create embedded.c from a set of static files for web 5 | server. This avoids the need for a ROMFS filesystem 6 | 7 | Andrew Tridgell 8 | May 2017 9 | ''' 10 | 11 | import sys 12 | 13 | out = open("embedded.c", "w") 14 | 15 | out.write(''' 16 | // generated embedded files for web_server 17 | #include 18 | 19 | struct embedded_file { 20 | const char *filename; 21 | unsigned size; 22 | const char *contents; 23 | }; 24 | 25 | ''') 26 | 27 | 28 | def embed_file(f, idx): 29 | '''embed one file''' 30 | contents = open(f).read() 31 | out.write(''' 32 | // %s 33 | static const char embedded_%u[] = {''' % (f, idx)) 34 | 35 | for c in contents: 36 | out.write('%u,' % ord(c)) 37 | out.write(''' 38 | 0}; 39 | ''') 40 | 41 | for i in range(1, len(sys.argv)): 42 | embed_file(sys.argv[i], i) 43 | 44 | out.write(''' 45 | const struct embedded_file embedded_files[] = { 46 | ''') 47 | 48 | for i in range(1, len(sys.argv)): 49 | print("Embedding file %s" % sys.argv[i]) 50 | out.write('{ "%s", sizeof(embedded_%u)-1, embedded_%u },\n' % (sys.argv[i], i, i)) 51 | 52 | out.write(''' 53 | { NULL, 0, NULL } 54 | }; 55 | ''') 56 | 57 | out.close() 58 | -------------------------------------------------------------------------------- /files/filesystem.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArduPilot 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

ArduPilot

14 | 15 |

ArduPilot Filesystem

16 | 17 |

Index of /

18 | 19 | 20 | 21 | 22 |
TypeNameDateSize
23 | 24 |

Directory size: MByte in files
25 | Card Size:  GByte
26 | Card Free:  GByte
27 | Card Label:
28 | 29 |

30 |
31 | 32 |


33 |

home 34 | 35 | 36 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /files/gen_manifest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ( 4 | echo "CACHE MANIFEST" 5 | echo "# "$(date) 6 | echo "CACHE:" 7 | echo / 8 | for f in *.html js/*.js images/*.svg images/*.png css/*.css data/*.xml; do 9 | echo $f 10 | done 11 | echo "NETWORK:" 12 | echo "*" 13 | ) > manifest.appcache 14 | -------------------------------------------------------------------------------- /files/gen_version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ( 4 | echo "#define SONIX_BUILD_DATE \"$(date +%Y-%m-%d)\"" 5 | echo "#define SONIX_GIT_REVISION \"$(git rev-parse HEAD | cut -c1-8)\"" 6 | ) > version.h 7 | 8 | -------------------------------------------------------------------------------- /files/images/greencopter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArduPilot/APWeb/ca1c15739b33ad7076cffb6017e5564e89e0e336/files/images/greencopter.png -------------------------------------------------------------------------------- /files/images/home.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /files/images/main_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArduPilot/APWeb/ca1c15739b33ad7076cffb6017e5564e89e0e336/files/images/main_logo.png -------------------------------------------------------------------------------- /files/images/redcopter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArduPilot/APWeb/ca1c15739b33ad7076cffb6017e5564e89e0e336/files/images/redcopter.png -------------------------------------------------------------------------------- /files/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArduPilot 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

ArduPilot

14 | 15 | 16 |

ArduPilot Web Server

17 | 18 | Welcome to the ArduPilot Web Interface!

19 | 20 |

30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /files/js/L.TileLayer.PouchDBCached.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | L.TileLayer.addInitHook(function() { 4 | 5 | if (!this.options.useCache) { 6 | this._db = null; 7 | this._canvas = null; 8 | return; 9 | } 10 | 11 | this._db = new PouchDB('offline-tiles'); 12 | this._canvas = document.createElement('canvas'); 13 | 14 | if (!(this._canvas.getContext && this._canvas.getContext('2d'))) { 15 | // HTML5 canvas is needed to pack the tiles as base64 data. If 16 | // the browser doesn't support canvas, the code will forcefully 17 | // skip caching the tiles. 18 | this._canvas = null; 19 | } 20 | }); 21 | 22 | // 🍂namespace TileLayer 23 | // 🍂section PouchDB tile caching options 24 | // 🍂option useCache: Boolean = false 25 | // Whether to use a PouchDB cache on this tile layer, or not 26 | L.TileLayer.prototype.options.useCache = false; 27 | 28 | // 🍂option saveToCache: Boolean = true 29 | // When caching is enabled, whether to save new tiles to the cache or not 30 | L.TileLayer.prototype.options.saveToCache = true; 31 | 32 | // 🍂option useOnlyCache: Boolean = false 33 | // When caching is enabled, whether to request new tiles from the network or not 34 | L.TileLayer.prototype.options.useOnlyCache = false; 35 | 36 | // 🍂option useCache: String = 'image/png' 37 | // The image format to be used when saving the tile images in the cache 38 | L.TileLayer.prototype.options.cacheFormat = 'image/png'; 39 | 40 | // 🍂option cacheMaxAge: Number = 24*3600*1000 41 | // Maximum age of the cache, in milliseconds 42 | L.TileLayer.prototype.options.cacheMaxAge = 24*3600*1000; 43 | 44 | 45 | L.TileLayer.include({ 46 | 47 | // Overwrites L.TileLayer.prototype.createTile 48 | createTile: function(coords, done) { 49 | var tile = document.createElement('img'); 50 | 51 | tile.onerror = L.bind(this._tileOnError, this, done, tile); 52 | 53 | if (this.options.crossOrigin) { 54 | tile.crossOrigin = ''; 55 | } 56 | 57 | /* 58 | Alt tag is *set to empty string to keep screen readers from reading URL and for compliance reasons 59 | http://www.w3.org/TR/WCAG20-TECHS/H67 60 | */ 61 | tile.alt = ''; 62 | 63 | var tileUrl = this.getTileUrl(coords); 64 | 65 | if (this.options.useCache && this._canvas) { 66 | this._db.get(tileUrl, {revs_info: true}, this._onCacheLookup(tile, tileUrl, done)); 67 | } else { 68 | // Fall back to standard behaviour 69 | tile.onload = L.bind(this._tileOnLoad, this, done, tile); 70 | } 71 | 72 | tile.src = tileUrl; 73 | return tile; 74 | }, 75 | 76 | // Returns a callback (closure over tile/key/originalSrc) to be run when the DB 77 | // backend is finished with a fetch operation. 78 | _onCacheLookup: function(tile, tileUrl, done) { 79 | return function(err, data) { 80 | if (data) { 81 | this.fire('tilecachehit', { 82 | tile: tile, 83 | url: tileUrl 84 | }); 85 | if (Date.now() > data.timestamp + this.options.cacheMaxAge && !this.options.useOnlyCache) { 86 | // Tile is too old, try to refresh it 87 | //console.log('Tile is too old: ', tileUrl); 88 | 89 | if (this.options.saveToCache) { 90 | tile.onload = L.bind(this._saveTile, this, tile, tileUrl, data._revs_info[0].rev, done); 91 | } 92 | tile.crossOrigin = 'Anonymous'; 93 | tile.src = tileUrl; 94 | tile.onerror = function(ev) { 95 | // If the tile is too old but couldn't be fetched from the network, 96 | // serve the one still in cache. 97 | this.src = data.dataUrl; 98 | } 99 | } else { 100 | // Serve tile from cached data 101 | //console.log('Tile is cached: ', tileUrl); 102 | tile.onload = L.bind(this._tileOnLoad, this, done, tile); 103 | tile.src = data.dataUrl; // data.dataUrl is already a base64-encoded PNG image. 104 | } 105 | } else { 106 | this.fire('tilecachemiss', { 107 | tile: tile, 108 | url: tileUrl 109 | }); 110 | if (this.options.useOnlyCache) { 111 | // Offline, not cached 112 | // console.log('Tile not in cache', tileUrl); 113 | tile.onload = L.Util.falseFn; 114 | tile.src = L.Util.emptyImageUrl; 115 | } else { 116 | //Online, not cached, request the tile normally 117 | // console.log('Requesting tile normally', tileUrl); 118 | if (this.options.saveToCache) { 119 | tile.onload = L.bind(this._saveTile, this, tile, tileUrl, null, done); 120 | } else { 121 | tile.onload = L.bind(this._tileOnLoad, this, done, tile); 122 | } 123 | tile.crossOrigin = 'Anonymous'; 124 | tile.src = tileUrl; 125 | } 126 | } 127 | }.bind(this); 128 | }, 129 | 130 | // Returns an event handler (closure over DB key), which runs 131 | // when the tile (which is an ) is ready. 132 | // The handler will delete the document from pouchDB if an existing revision is passed. 133 | // This will keep just the latest valid copy of the image in the cache. 134 | _saveTile: function(tile, tileUrl, existingRevision, done) { 135 | if (this._canvas === null) return; 136 | this._canvas.width = tile.naturalWidth || tile.width; 137 | this._canvas.height = tile.naturalHeight || tile.height; 138 | 139 | var context = this._canvas.getContext('2d'); 140 | context.drawImage(tile, 0, 0); 141 | 142 | var dataUrl; 143 | try { 144 | dataUrl = this._canvas.toDataURL(this.options.cacheFormat); 145 | } catch(err) { 146 | this.fire('tilecacheerror', { tile: tile, error: err }); 147 | return done(); 148 | } 149 | var doc = {dataUrl: dataUrl, timestamp: Date.now()}; 150 | 151 | if (existingRevision) { 152 | this._db.remove(tileUrl, existingRevision); 153 | } 154 | /// FIXME: There is a deprecation warning about parameters in the 155 | /// this._db.put() call. 156 | this._db.put(doc, tileUrl, doc.timestamp); 157 | 158 | if (done) { done(); } 159 | }, 160 | 161 | // 🍂section PouchDB tile caching options 162 | // 🍂method seed(bbox: LatLngBounds, minZoom: Number, maxZoom: Number): this 163 | // Starts seeding the cache given a bounding box and the minimum/maximum zoom levels 164 | // Use with care! This can spawn thousands of requests and flood tileservers! 165 | seed: function(bbox, minZoom, maxZoom) { 166 | if (!this.options.useCache) return; 167 | if (minZoom > maxZoom) return; 168 | if (!this._map) return; 169 | 170 | var queue = []; 171 | 172 | for (var z = minZoom; z<=maxZoom; z++) { 173 | 174 | var northEastPoint = this._map.project(bbox.getNorthEast(),z); 175 | var southWestPoint = this._map.project(bbox.getSouthWest(),z); 176 | 177 | // Calculate tile indexes as per L.TileLayer._update and 178 | // L.TileLayer._addTilesFromCenterOut 179 | var tileSize = this.getTileSize(); 180 | var tileBounds = L.bounds( 181 | L.point(Math.floor(northEastPoint.x / tileSize.x), Math.floor(northEastPoint.y / tileSize.y)), 182 | L.point(Math.floor(southWestPoint.x / tileSize.x), Math.floor(southWestPoint.y / tileSize.y))); 183 | 184 | for (var j = tileBounds.min.y; j <= tileBounds.max.y; j++) { 185 | for (var i = tileBounds.min.x; i <= tileBounds.max.x; i++) { 186 | point = new L.Point(i, j); 187 | point.z = z; 188 | queue.push(this._getTileUrl(point)); 189 | } 190 | } 191 | } 192 | 193 | var seedData = { 194 | bbox: bbox, 195 | minZoom: minZoom, 196 | maxZoom: maxZoom, 197 | queueLength: queue.length 198 | } 199 | this.fire('seedstart', seedData); 200 | var tile = this._createTile(); 201 | tile._layer = this; 202 | this._seedOneTile(tile, queue, seedData); 203 | return this; 204 | }, 205 | 206 | _createTile: function () { 207 | return document.createElement('img'); 208 | }, 209 | 210 | // Modified L.TileLayer.getTileUrl, this will use the zoom given by the parameter coords 211 | // instead of the maps current zoomlevel. 212 | _getTileUrl: function (coords) { 213 | var zoom = coords.z; 214 | if (this.options.zoomReverse) { 215 | zoom = this.options.maxZoom - zoom; 216 | } 217 | zoom += this.options.zoomOffset; 218 | return L.Util.template(this._url, L.extend({ 219 | r: this.options.detectRetina && L.Browser.retina && this.options.maxZoom > 0 ? '@2x' : '', 220 | s: this._getSubdomain(coords), 221 | x: coords.x, 222 | y: this.options.tms ? this._globalTileRange.max.y - coords.y : coords.y, 223 | z: this.options.maxNativeZoom ? Math.min(zoom, this.options.maxNativeZoom) : zoom 224 | }, this.options)); 225 | }, 226 | 227 | // Uses a defined tile to eat through one item in the queue and 228 | // asynchronously recursively call itself when the tile has 229 | // finished loading. 230 | _seedOneTile: function(tile, remaining, seedData) { 231 | if (!remaining.length) { 232 | this.fire('seedend', seedData); 233 | return; 234 | } 235 | this.fire('seedprogress', { 236 | bbox: seedData.bbox, 237 | minZoom: seedData.minZoom, 238 | maxZoom: seedData.maxZoom, 239 | queueLength: seedData.queueLength, 240 | remainingLength: remaining.length 241 | }); 242 | 243 | var url = remaining.pop(); 244 | 245 | this._db.get(url, function(err, data) { 246 | if (!data) { 247 | /// FIXME: Do something on tile error!! 248 | tile.onload = function(ev) { 249 | this._saveTile(tile, url, null); //(ev) 250 | this._seedOneTile(tile, remaining, seedData); 251 | }.bind(this); 252 | tile.crossOrigin = 'Anonymous'; 253 | tile.src = url; 254 | } else { 255 | this._seedOneTile(tile, remaining, seedData); 256 | } 257 | }.bind(this)); 258 | 259 | } 260 | 261 | }); 262 | 263 | 264 | -------------------------------------------------------------------------------- /files/js/charts.js: -------------------------------------------------------------------------------- 1 | var charts = {}; 2 | var chart_lines = {}; 3 | 4 | function chart_range(range) { 5 | var round_val = Math.round(((range.max+5) - (range.min-5))/7); 6 | var max = Math.round(range.max+round_val); 7 | var min = Math.round(range.min-round_val); 8 | return {min: min, max: max}; 9 | } 10 | 11 | // create a new chart with a list of MAVLink variables as lines 12 | function create_chart(canvass_name, variables) { 13 | var colors = [ "red", "green", "blue", "orange" ]; 14 | var settings = { grid : { fillStyle: "DarkGrey" }, 15 | yRangeFunction : chart_range }; 16 | charts[canvass_name] = new SmoothieChart(settings); 17 | for (var i=0; i= 0) { 57 | value = value.toFixed(fixed); 58 | } 59 | return value; 60 | } 61 | 62 | var mavlink_msg_types = [] 63 | 64 | /* 65 | fill in all divs of form MAVLINK:MSGNAME:field at refresh_ms() rate 66 | */ 67 | function fill_mavlink_ids(options={}) { 68 | function again() { 69 | setTimeout(function() { fill_mavlink_ids(options); }, refresh_ms()); 70 | } 71 | if (mavlink_msg_types.length == 0) { 72 | /* 73 | work out what mavlink messages we need to fetch by looking 74 | through all names 75 | */ 76 | if ('extra_msgs' in options) { 77 | mavlink_msg_types = options.extra_msgs; 78 | } 79 | var divs = document.querySelectorAll('[name^="MAVLINK:"]'); 80 | var numdivs = divs.length; 81 | for (var i = 0; i < numdivs; i++) { 82 | var divname = divs[i].attributes.name.value; 83 | var x = divname.split(":"); 84 | var msg_name = x[1]; 85 | if (!mavlink_msg_types.includes(msg_name)) { 86 | mavlink_msg_types.push(msg_name); 87 | } 88 | } 89 | } 90 | var xhr = createCORSRequest("POST", drone_url + "/ajax/command.json"); 91 | var form = new FormData(); 92 | form.append('command1', 'mavlink_message(' + mavlink_msg_types.join() + ')'); 93 | xhr.onload = function() { 94 | again(); 95 | var mavlink; 96 | var text = xhr.responseText; 97 | text = text.replace(/(\r\n|\n|\r)/gm," "); 98 | try { 99 | mavlink = JSON.parse(text); 100 | } catch(e) { 101 | console.log(e); 102 | return; 103 | } 104 | var chart_lines = {} 105 | if ('chart_lines' in options) { 106 | chart_lines = options.chart_lines; 107 | } 108 | for (var i=0, len=mavlink_msg_types.length; i 0 || fname in chart_lines) { 114 | var value = scale_variable(fname, mavlink[msg][v]); 115 | } 116 | for (var j=0; j 2*refresh_ms()) { 144 | // cope with bad image downloads 145 | refresh_camera(); 146 | } 147 | } 148 | } 149 | 150 | 151 | /* 152 | fill variables in a page from json 153 | */ 154 | function page_fill_json_value(json) { 155 | for (var v in json) { 156 | var element = document.getElementById(v); 157 | if (element) { 158 | element.value = json[v]; 159 | } 160 | } 161 | } 162 | 163 | /* 164 | fill html in a page from json 165 | */ 166 | function page_fill_json_html(json) { 167 | for (var v in json) { 168 | var element = document.getElementById(v); 169 | if (element) { 170 | element.innerHTML = json[v]; 171 | } 172 | } 173 | } 174 | 175 | /* 176 | send a command function 177 | */ 178 | function command_send(command, options={}) { 179 | var args = Array.prototype.slice.call(arguments); 180 | var xhr = createCORSRequest("POST", drone_url + "/ajax/command.json"); 181 | var form = new FormData(); 182 | if (options['onload']) { 183 | xhr.onload = function() { 184 | options.onload(xhr.responseText); 185 | } 186 | } 187 | if (options['onerror']) { 188 | xhr.timeout = 3000; 189 | if (options['timeout']) { 190 | xhr.timeout = options['timeout']; 191 | } 192 | xhr.onerror = function() { 193 | options.onerror(); 194 | } 195 | xhr.ontimeout = function() { 196 | options.onerror(); 197 | } 198 | } 199 | if (options['fillid']) { 200 | xhr.onload = function() { 201 | var fillid = options['fillid']; 202 | var element = document.getElementById(options.fillid); 203 | if (element) { 204 | element.innerHTML = xhr.responseText; 205 | } 206 | } 207 | } 208 | if (options['filljson'] == true) { 209 | xhr.onload = function() { 210 | try { 211 | var fill = JSON.parse(xhr.responseText); 212 | } catch(e) { 213 | console.log(e); 214 | return; 215 | } 216 | page_fill_json_value(fill); 217 | } 218 | } 219 | if (options['extra_args']) { 220 | var extra = options['extra_args']; 221 | for (var k in extra) { 222 | form.append(k, extra[k]); 223 | } 224 | } 225 | if (command.constructor === Array) { 226 | // allow for multiple commands 227 | for (var i=0; i' + message + ''; 376 | } 377 | } 378 | 379 | /* 380 | append a message in a div by id, with given color 381 | */ 382 | function append_message_color(id, color, message) { 383 | var element = document.getElementById(id); 384 | if (element) { 385 | element.innerHTML += '
' + message + ''; 386 | } 387 | mavlink_statustext(MAV_SEVERITY_INFO, message); 388 | } 389 | 390 | /* 391 | get utc time in seconds 392 | */ 393 | function get_utc_sec() { 394 | var d = new Date(); 395 | var dsec = d.getTime() / 1000; 396 | return dsec; 397 | } 398 | 399 | /* 400 | set the date on the sonix board 401 | */ 402 | function set_sonix_date() 403 | { 404 | var d = new Date(); 405 | var dsec = get_utc_sec(); 406 | var tz_offset = -d.getTimezoneOffset() * 60; 407 | d = (dsec+0.5).toFixed(0); 408 | var cmd = "set_time_utc(" + d + "," + tz_offset + ")"; 409 | command_send(cmd); 410 | } 411 | 412 | // set date every 20s 413 | setInterval(set_sonix_date(), 20000); 414 | -------------------------------------------------------------------------------- /files/js/smoothie.min.js: -------------------------------------------------------------------------------- 1 | (function(exports){var Util={extend:function(){arguments[0]=arguments[0]||{};for(var i=1;ithis.maxValue){this.maxValue=value}if(value=0&&this.data[i][0]>timestamp){i--}if(i===-1){this.data.splice(0,0,[timestamp,value])}else if(this.data.length>0&&this.data[i][0]===timestamp){if(sumRepeatedTimeStampValues){this.data[i][1]+=value;value=this.data[i][1]}else{this.data[i][1]=value}}else if(i=maxDataSetLength&&this.data[removeCount+1][0]0){timeSeries.resetBoundsTimerId=setInterval(function(){timeSeries.resetBounds()},timeSeries.options.resetBoundsInterval)}};SmoothieChart.prototype.removeTimeSeries=function(timeSeries){var numSeries=this.seriesSet.length;for(var i=0;i.1||Math.abs(minValueDiff)>.1;this.currentValueRange+=chartOptions.scaleSmoothing*valueRangeDiff;this.currentVisMinValue+=chartOptions.scaleSmoothing*minValueDiff}this.valueRange={min:chartMinValue,max:chartMaxValue}};SmoothieChart.prototype.render=function(canvas,time){var nowMillis=(new Date).getTime();if(!this.isAnimatingScale){var maxIdleMillis=Math.min(1e3/6,this.options.millisPerPixel);if(nowMillis-this.lastRenderTimeMillis0){context.beginPath();for(var t=time-time%chartOptions.grid.millisPerLine;t>=oldestValidTime;t-=chartOptions.grid.millisPerLine){var gx=timeToXPixel(t);if(chartOptions.grid.sharpLines){gx-=.5}context.moveTo(gx,0);context.lineTo(gx,dimensions.height)}context.stroke();context.closePath()}for(var v=1;v1){if(seriesOptions.fillStyle){context.lineTo(dimensions.width+seriesOptions.lineWidth+1,lastY);context.lineTo(dimensions.width+seriesOptions.lineWidth+1,dimensions.height+seriesOptions.lineWidth+1);context.lineTo(firstX,dimensions.height+seriesOptions.lineWidth);context.fillStyle=seriesOptions.fillStyle;context.fill()}if(seriesOptions.strokeStyle&&seriesOptions.strokeStyle!=="none"){context.stroke()}context.closePath()}context.restore()}if(!chartOptions.labels.disabled&&!isNaN(this.valueRange.min)&&!isNaN(this.valueRange.max)){var maxValueString=chartOptions.yMaxFormatter(this.valueRange.max,chartOptions.labels.precision),minValueString=chartOptions.yMinFormatter(this.valueRange.min,chartOptions.labels.precision),maxLabelPos=chartOptions.scrollBackwards?0:dimensions.width-context.measureText(maxValueString).width-2,minLabelPos=chartOptions.scrollBackwards?0:dimensions.width-context.measureText(minValueString).width-2;context.fillStyle=chartOptions.labels.fillStyle;context.fillText(maxValueString,maxLabelPos,chartOptions.labels.fontSize);context.fillText(minValueString,minLabelPos,dimensions.height-2)}if(chartOptions.timestampFormatter&&chartOptions.grid.millisPerLine>0){var textUntilX=chartOptions.scrollBackwards?context.measureText(minValueString).width:dimensions.width-context.measureText(minValueString).width+4;for(var t=time-time%chartOptions.grid.millisPerLine;t>=oldestValidTime;t-=chartOptions.grid.millisPerLine){var gx=timeToXPixel(t);if(!chartOptions.scrollBackwards&&gxtextUntilX){var tx=new Date(t),ts=chartOptions.timestampFormatter(tx),tsWidth=context.measureText(ts).width;textUntilX=chartOptions.scrollBackwards?gx+tsWidth+2:gx-tsWidth-2;context.fillStyle=chartOptions.labels.fillStyle;if(chartOptions.scrollBackwards){context.fillText(ts,gx,dimensions.height-2)}else{context.fillText(ts,gx-tsWidth,dimensions.height-2)}}}}context.restore()};SmoothieChart.timeFormatter=function(date){function pad2(number){return(number<10?"0":"")+number}return pad2(date.getHours())+":"+pad2(date.getMinutes())+":"+pad2(date.getSeconds())};exports.TimeSeries=TimeSeries;exports.SmoothieChart=SmoothieChart})(typeof exports==="undefined"?this:exports); -------------------------------------------------------------------------------- /files/js/tabs.js: -------------------------------------------------------------------------------- 1 | /* 2 | handle tabbed interface 3 | */ 4 | var activeTab = null; 5 | 6 | // open a tab 7 | function openTab(evt, tabName) { 8 | // Declare all variables 9 | var i, tabcontent, tablinks; 10 | 11 | // Get all elements with class="tabcontent" and hide them 12 | tabcontent = document.getElementsByClassName("tabcontent"); 13 | for (i = 0; i < tabcontent.length; i++) { 14 | tabcontent[i].style.display = "none"; 15 | } 16 | 17 | // Get all elements with class="tablinks" and remove the class "active" 18 | tablinks = document.getElementsByClassName("tablinks"); 19 | for (i = 0; i < tablinks.length; i++) { 20 | tablinks[i].className = tablinks[i].className.replace(" active", ""); 21 | } 22 | 23 | // Show the current tab, and add an "active" class to the button that opened the tab 24 | document.getElementById(tabName).style.display = "block"; 25 | evt.currentTarget.className += " active"; 26 | 27 | activeTab = tabName; 28 | 29 | tab_clicked(activeTab); 30 | } 31 | -------------------------------------------------------------------------------- /files/js/ublox.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | library to upload MGA data to uBlox GPS for fast fix 4 | */ 5 | 6 | var mga_poll_period = 5000; 7 | var last_pos = null; 8 | var sent_last_pos = false; 9 | 10 | function set_last_pos(pos) { 11 | last_pos = pos; 12 | } 13 | 14 | /* 15 | handle downloaded MGA data 16 | */ 17 | function handle_mga_data(data) { 18 | var utc_sec = get_utc_sec(); 19 | var new_data = { timestamp : utc_sec, data : data }; 20 | db_store("mga_data", new_data); 21 | set_mga_data(new_data); 22 | } 23 | 24 | /* 25 | download MGA data and store into the database 26 | */ 27 | function download_mga_data() { 28 | console.log("Downloading mga data from " + mga_data_url); 29 | ajax_get_callback_binary(mga_data_url, handle_mga_data); 30 | } 31 | 32 | /* 33 | called by database when it has fetched mga_data 34 | */ 35 | function set_mga_data(data) { 36 | console.log("mga_data length " + data.data.byteLength); 37 | var utc_sec = get_utc_sec(); 38 | var age_days = (utc_sec - data.timestamp) / (60*60*24); 39 | console.log("mga data age: " + age_days); 40 | if (age_days > 2) { 41 | download_mga_data(); 42 | } 43 | var formData = new FormData(); 44 | formData.append("command1", "mga_upload()"); 45 | formData.append("mga_data", new Blob([data.data], { type : "data" })); 46 | if (last_pos != null && last_pos.length == 3) { 47 | console.log("Sending pos: " + last_pos); 48 | formData.append("latitude", last_pos[0]*1e7); 49 | formData.append("longitude", last_pos[1]*1e7); 50 | formData.append("altitude", last_pos[2]); 51 | formData.append("utc_time", utc_sec); 52 | sent_last_pos = true; 53 | } 54 | 55 | var xhr = createCORSRequest("POST", drone_url + "/ajax/command.json"); 56 | xhr.send(formData); 57 | setTimeout(poll_ublox_status, mga_poll_period); 58 | } 59 | 60 | function check_mga_status(json) { 61 | try { 62 | mga_status = JSON.parse(json); 63 | } catch(e) { 64 | console.log(e); 65 | setTimeout(poll_ublox_status, mga_poll_period); 66 | return; 67 | } 68 | page_fill_json_html(mga_status); 69 | var utc_sec = get_utc_sec(); 70 | if (mga_status.offline_cache_size > 0 && 71 | (last_pos == null || sent_last_pos)) { 72 | // its all up to date, don't send it the data 73 | setTimeout(poll_ublox_status, mga_poll_period); 74 | 75 | // set the date if needed 76 | if (mga_status.fc_time < utc_sec - 10*60) { 77 | set_sonix_date(); 78 | } 79 | return; 80 | } 81 | db_fetch_onerror('mga_data', set_mga_data, download_mga_data); 82 | } 83 | 84 | function poll_ublox_status() { 85 | if (last_pos == null) { 86 | db_fetch('last_position', set_last_pos); 87 | } 88 | 89 | command_send("mga_status()", { onload : check_mga_status }); 90 | } 91 | -------------------------------------------------------------------------------- /files/map.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArduPilot 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |

ArduPilot

23 | 24 |
25 | 26 |

Position:

27 | 28 | 197 | 198 |


199 |

home 200 | 201 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /files/nvram.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArduPilot 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

ArduPilot

14 | 15 |

Video Parameters

16 | 17 | It is not recommended that you change any video NVRAM parameters 18 | unless you are instructed to do so. This parameter interface is 19 | intended for engineering development.

20 | 21 | NVRAM Pack: 22 | 25 | 26 |

27 | 28 |

29 | 30 | 31 | 32 |
NameTypeSizeValue
33 | 34 |
35 |

home 36 | 37 | 38 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /files/parameters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArduPilot 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

ArduPilot

14 | 15 | Parameter Category:  16 | 18 | 19 |

  20 | 21 | 22 |

23 | 24 | 25 | 26 |
NameValueOptionsFull NameDescription
27 | 28 |
29 |

home 30 | 31 | 32 | 311 | 312 | 313 | -------------------------------------------------------------------------------- /files/status.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArduPilot 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |

ArduPilot

20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | 33 |
34 | 35 | Core system information

36 | 37 |

System Information

38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 |
VariableValue
Uptime
Free Memory (KERNEL)
Free Memory (DMA)
FC MAVLink count
FC MAVLink baudrate
47 | 48 |

Flight Board Status

49 | 50 | 51 | 52 | 53 | 54 |
Mode
Uptime
Voltage
55 |
56 | 57 |
58 |

Attitude

59 | 60 | 61 | 92 | 100 | 101 |
62 | 63 | 64 | 65 | 66 | 67 | 68 |
VariableValue
Roll
Pitch
Yaw
69 | 70 |

IMU Status

71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 |
SensorXYZ
Accelerometer
Gyroscope
Magnetometer
90 | 91 |
93 | Accelerometers (X, Y, Z)
94 |

95 | Gyro (roll, pitch, yaw)
96 |

97 | Magnetometer (X, Y, Z)
98 |

99 |

102 |
103 | 104 | 105 |
106 | 107 | 108 | 117 | 123 | 124 |
109 | 110 | 111 | 112 | 113 | 114 |
VariableValue
Pressure Absolute
Pressure Relative
Temperature
115 | 116 |
118 | Pressure
119 |

120 | Temperature
121 |

122 |

125 | 126 |
127 | 128 |
129 | 130 | 131 | 152 | 157 | 158 |
132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 |
VariableValue
FixType
Numsats
EKF Status
Latitude (GPS)
Longitude (GPS)
Altitude (AMSL)
Speed Accuracy
Position Accuracy
Height Accuracy
MGA Cache Size
MGA Upload UTC
MGA Highest UTC
Fix start
Fix time
Pos source
Time source
151 |
153 | GPS Accuracy 154 | (speed, horizontal, vertical)
155 |

156 |

159 |
160 | 161 |
162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 |
VariableValue
Flags
Latitude
Longitude
Altitude
Velocity Variance
Position Variance (H)
Position Variance (V)
Compass Variance
174 |
175 | 176 |
177 | 178 | 179 | 195 | 199 | 200 |
180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 |
VariableValue
Status
Channels
Chan1
Chan2
Chan3
Chan4
Chan5
Chan6
Chan7
RX PPS
RX RSSI
194 |
196 | Stick input (chan1, chan2, chan3, chan4)
197 |

198 |

201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 |
ButtonStatus
Left Button
Right Button
Power Button
Left Shoulder Button
Right Shoulder Button
210 |

Bind Transmitter

211 | 212 | To bind your transmitter, first press the button below, then power on 213 | your transmitter with the left button pressed. 214 |

The flight controller will wait for a bind packet from the transmitter. 215 |

The transmitter will send bind packets for 5 seconds if powered on with left button pressed.

216 | 217 | 218 | 219 |

220 | 221 |
222 | snapshot 223 |

224 |

225 | JPG Frames 226 | MJPG Stream
227 |
228 | 229 | 230 | 231 | 282 |
283 | 284 |
285 | 286 |

Motor Testing

287 | 299 | 300 | Select motor to test, along with test power level (as a percentage) 301 | and test time in seconds. If you choose "All Motors" then all 4 motors 302 | will come on in sequence, starting with the front-right motor and 303 | proceeding clockwise from there. 304 | 305 |

306 | 307 | 308 | 309 | 310 | 311 | 312 |

313 | Test Power 314 | 326 | 327 |
Test Time (s) 328 | 340 | 341 |

342 | 343 |
344 | 345 |

Flight Controller Messages

346 | 347 | 348 | 349 |
350 | 351 | Refresh rate:  352 | 364 | 365 |
366 |

home 367 | 368 | 481 | 482 | 483 | 484 | 485 | -------------------------------------------------------------------------------- /files/system.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArduPilot 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

ArduPilot

14 | 15 |

System Control

16 | 17 | Control system setup of your vehicle 18 |

19 |

20 |

21 | 22 |


23 |

Video Control

24 | 25 | Toggle video recording. Videos will be saved in the filesystem.

26 | 27 | 28 | 29 | 30 |


31 |

WiFi Setup

32 | 33 | Configure your WiFi SSID, password, authentication type and 34 | channel. 35 | 36 |

If using WEP you need to use a 5 character password. For WPA2 your 37 | password needs to be at least 8 characters long. Using WPA2 is 38 | recommended.

39 | 40 |

41 | WiFi Configuration 42 | WiFi SSID:
43 |
44 | WiFi Password:
45 | 46 | Show password
47 | Auth Type: 48 | 54 | WiFi Channel: 55 | 71 |
72 |
73 | 74 | 88 | 89 |
90 |

Reboot

91 | 92 | Reboot your video board or flight controller board

93 | 94 | 95 | 96 | 97 |


98 |

Bind Transmitter

99 | 100 | To bind your transmitter, first press the button below, then power on 101 | your transmitter with the left button pressed. 102 |

The flight controller will wait for a bind packet from the transmitter. 103 |

The transmitter will send bind packets for 5 seconds if powered on with left button pressed.

104 | 105 | 106 | 107 |


108 |

Factory Reset

109 | 110 | This allows you to reset your video board and flight board to factory 111 | defaults. The WiFi password after reset will be '12345678'. To secure 112 | your ArduPilot you should change your WiFi password immediately after 113 | rebooting.

114 | 115 |

NOTE! You must first power-cycle and then re-calibrate after a 116 | reset or you will not be able to arm.

117 | 118 | 119 | 120 |


121 |

Format

122 | 123 | Format the microSD card in your video board as FAT32. The microSD 124 | card must be at least 4G in size, and not more than 32G.

125 | 126 | 127 | 128 |


129 |

home 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /files/upgrade.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ArduPilot 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

ArduPilot

14 | 15 |
16 | 17 |
18 |

Upload progress:
19 |

20 |
21 |
22 |
23 | 24 |

Firmware update

25 | 26 | Upgrade the firmware on your vehicle. 27 | 28 |

You can get the latest firmware releases from http://firmware.ardupilot.org/ 29 | 30 |


31 |

Upgrade Sonix Firmware

32 | 33 | After uploading the new Sonix firmware (which should have a file 34 | extension of ".bin"), the Sonix board will automatically reboot and 35 | start the new firmware. You will need to reconnect to the WiFi access 36 | point on the ArduPilot after the reboot.

37 | 38 | Choose a file to upload:
39 | 40 | 41 |

42 | Current version: unknown unknown 43 | 44 |


45 |

Upgrade ArduPilot Firmware

46 | 47 | After uploading the new ArduPilot firmware (which should have a file 48 | extension of ".abin") the flight controller will automatically 49 | reboot. The reboot will take about 15 seconds.

50 | 51 | Choose a file to upload:
52 | 53 | 54 |

55 | Current version: unknown unknown 56 | 57 |


58 |

Upgrade Transmitter Firmware

59 | 60 | After uploading the new transmitter firmware (which should have a file 61 | extension of ".img"), you will need to power cycle your 62 | transmitter. The transmitter will then flash its LEDs alternately at a 63 | one second interval for 14 seconds while it performs the firmware 64 | update. It will then boot as normal.

65 | 66 | Choose a file to upload:
67 | 68 | 69 |

70 | Current version: unknown 71 | 72 |


73 |

File Upload

74 | 75 | You can upload a file to any location on the microSD card.

76 | 77 |
78 | Choose a file to upload:
79 | 80 | 81 |


82 |

home 83 | 84 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /functions.h: -------------------------------------------------------------------------------- 1 | /* 2 | provide server side functions in C 3 | */ 4 | 5 | void functions_init(struct template_state *tmpl); 6 | -------------------------------------------------------------------------------- /html/manifest.appcache: -------------------------------------------------------------------------------- 1 | CACHE MANIFEST 2 | # Friday 9 June 15:36:03 AEST 2017 3 | CACHE: 4 | / 5 | calibration.html 6 | filesystem.html 7 | index.html 8 | map.html 9 | nvram.html 10 | parameters.html 11 | status.html 12 | system.html 13 | upgrade.html 14 | js/charts.js 15 | js/config.js 16 | js/cors.js 17 | js/database.js 18 | js/leaflet.js 19 | js/leaflet.rotatedMarker.js 20 | js/L.TileLayer.PouchDBCached.js 21 | js/mavlink.js 22 | js/pouchdb.js 23 | js/smoothie.min.js 24 | js/tabs.js 25 | js/ublox.js 26 | images/home.svg 27 | images/main_logo.svg 28 | images/greencopter.png 29 | images/main_logo.png 30 | images/redcopter.png 31 | css/leaflet.css 32 | css/styles.css 33 | data/apm.pdef.xml 34 | NETWORK: 35 | * 36 | -------------------------------------------------------------------------------- /html/version.h: -------------------------------------------------------------------------------- 1 | #define SONIX_BUILD_DATE "2017-06-09" 2 | #define SONIX_GIT_REVISION "aa12d1e0" 3 | -------------------------------------------------------------------------------- /includes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef SYSTEM_FREERTOS 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "socket_ctrl.h" 17 | #include 18 | #include "../talloc.h" 19 | #include "../dev_console.h" 20 | #include "../util/print_vprintf.h" 21 | #else 22 | #include "linux/includes.h" 23 | #include "mavlink_core.h" 24 | #endif 25 | 26 | #include "cgi.h" 27 | #include "template.h" 28 | #include "web_files.h" 29 | 30 | 31 | -------------------------------------------------------------------------------- /linux/includes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "util_linux.h" 33 | #include "mavlink_linux.h" 34 | -------------------------------------------------------------------------------- /linux/mavlink_linux.c: -------------------------------------------------------------------------------- 1 | /* 2 | mavlink utility functions for web_server 3 | */ 4 | 5 | #include "../includes.h" 6 | 7 | /* 8 | list of packet types received 9 | */ 10 | struct mavlink_packet { 11 | struct mavlink_packet *next; 12 | const char *name; 13 | mavlink_message_t msg; 14 | uint32_t receive_ms; 15 | }; 16 | 17 | 18 | static struct mavlink_packet *mavlink_packets; 19 | 20 | /* 21 | list of parameters received 22 | */ 23 | struct param_packet { 24 | struct param_packet *next; 25 | char name[17]; 26 | float value; 27 | }; 28 | 29 | 30 | // we have a param list per letter, to reduce list traversal cost 31 | static struct param_packet *param_packets[26]; 32 | static uint32_t param_count; 33 | static uint32_t param_expected_count; 34 | static uint32_t param_last_value_sec; 35 | 36 | /* 37 | list of command acks received 38 | */ 39 | struct mavlink_command_ack { 40 | struct mavlink_command_ack *next; 41 | uint32_t receive_ms; 42 | uint16_t command; 43 | uint8_t result; 44 | }; 45 | 46 | static struct mavlink_command_ack *command_acks; 47 | 48 | /* 49 | send a request to set stream rates 50 | */ 51 | static void send_stream_rates_request(uint8_t rate) 52 | { 53 | mavlink_msg_request_data_stream_send(MAVLINK_COMM_FC, 54 | MAVLINK_TARGET_SYSTEM_ID, 55 | 0, 56 | MAV_DATA_STREAM_ALL, 57 | rate, 1); 58 | } 59 | /* 60 | periodic mavlink tasks - run on each HEARTBEAT from fc 61 | */ 62 | static void mavlink_periodic(void) 63 | { 64 | long long now = get_sys_seconds_boot(); 65 | static long long last_send_stream; 66 | static long long last_heartbeat; 67 | 68 | if (now - last_send_stream > 15) { 69 | send_stream_rates_request(4); 70 | last_send_stream = now; 71 | } 72 | if (now - last_heartbeat > 10 || last_heartbeat == 0) { 73 | console_printf("heartbeat ok\n"); 74 | } 75 | 76 | if (param_count == 0 || 77 | (param_expected_count > param_count && 78 | get_sys_seconds_boot() - param_last_value_sec > 20)) { 79 | console_printf("requesting parameters param_count=%u param_expected_count=%u\n", 80 | param_count, param_expected_count); 81 | mavlink_msg_param_request_list_send(MAVLINK_COMM_FC, 82 | MAVLINK_TARGET_SYSTEM_ID, 83 | 0); 84 | } 85 | 86 | last_heartbeat = now; 87 | } 88 | 89 | extern const char *mavlink_message_name(const mavlink_message_t *msg); 90 | 91 | /* 92 | save a param value 93 | */ 94 | static void param_save_packet(const mavlink_message_t *msg) 95 | { 96 | mavlink_param_value_t param_pkt; 97 | mavlink_msg_param_value_decode(msg, ¶m_pkt); 98 | if (param_pkt.param_id[0] < 'A' || param_pkt.param_id[0] > 'Z') { 99 | // invalid name 100 | return; 101 | } 102 | struct param_packet *p0 = param_packets[param_pkt.param_id[0] - 'A']; 103 | struct param_packet *p; 104 | for (p=p0; p; p=p->next) { 105 | if (strncmp(p->name, param_pkt.param_id, 16) == 0) { 106 | p->value = param_pkt.param_value; 107 | param_last_value_sec = get_sys_seconds_boot(); 108 | if (param_pkt.param_count < 30000 && 109 | param_pkt.param_count > 0 && 110 | param_pkt.param_count > param_expected_count) { 111 | param_expected_count = param_pkt.param_count-1; 112 | } 113 | return; 114 | } 115 | } 116 | p = talloc_size(NULL, sizeof(*p)); 117 | if (p) { 118 | strncpy(p->name, param_pkt.param_id, 16); 119 | p->name[16] = 0; 120 | p->value = param_pkt.param_value; 121 | p->next = p0; 122 | param_packets[param_pkt.param_id[0] - 'A'] = p; 123 | param_count++; 124 | param_last_value_sec = get_sys_seconds_boot(); 125 | if (param_pkt.param_count < 30000 && 126 | param_pkt.param_count > 0 && 127 | param_pkt.param_count > param_expected_count) { 128 | param_expected_count = param_pkt.param_count-1; 129 | } 130 | } 131 | } 132 | 133 | /* 134 | get a parameter value 135 | */ 136 | bool mavlink_param_get(const char *name, float *value) 137 | { 138 | if (name[0] < 'A' || name[0] > 'Z') { 139 | return false; 140 | } 141 | struct param_packet *p0 = param_packets[name[0] - 'A']; 142 | struct param_packet *p; 143 | for (p=p0; p; p=p->next) { 144 | if (strcmp(p->name, name) == 0) { 145 | *value = p->value; 146 | return true; 147 | } 148 | } 149 | return false; 150 | } 151 | 152 | /* 153 | list all parameters as json 154 | */ 155 | void mavlink_param_list_json(struct sock_buf *sock, const char *prefix, bool *first) 156 | { 157 | uint8_t c; 158 | uint8_t plen = strlen(prefix); 159 | 160 | for (c=0; c<26; c++) { 161 | struct param_packet *p0 = param_packets[c]; 162 | struct param_packet *p; 163 | for (p=p0; p; p=p->next) { 164 | if (strncmp(p->name, prefix, plen) != 0) { 165 | continue; 166 | } 167 | char *vstr = print_printf(sock, "%f ", p->value); 168 | if (vstr == NULL) { 169 | continue; 170 | } 171 | // ensure it is null terminated 172 | vstr[talloc_get_size(vstr)-1] = 0; 173 | if (vstr[strlen(vstr)-1] == '.') { 174 | // can't have trailing . in javascript float for json 175 | vstr[strlen(vstr)-1] = 0; 176 | } 177 | if (!*first) { 178 | sock_printf(sock, ",\r\n"); 179 | } 180 | *first = false; 181 | sock_printf(sock, "{ \"name\" : \"%s\", \"value\" : %s }", 182 | p->name, vstr); 183 | talloc_free(vstr); 184 | } 185 | } 186 | } 187 | 188 | /* 189 | save last instance of each packet type 190 | */ 191 | static void mavlink_save_packet(const mavlink_message_t *msg) 192 | { 193 | if (msg->msgid == MAVLINK_MSG_ID_PARAM_VALUE) { 194 | param_save_packet(msg); 195 | } 196 | struct mavlink_packet *p; 197 | for (p=mavlink_packets; p; p=p->next) { 198 | if (p->msg.msgid == msg->msgid) { 199 | memcpy(&p->msg, msg, sizeof(mavlink_message_t)); 200 | p->receive_ms = get_time_boot_ms(); 201 | return; 202 | } 203 | } 204 | p = talloc_size(NULL, sizeof(*p)); 205 | if (p == NULL) { 206 | return; 207 | } 208 | p->next = mavlink_packets; 209 | p->name = mavlink_message_name(msg); 210 | memcpy(&p->msg, msg, sizeof(mavlink_message_t)); 211 | p->receive_ms = get_time_boot_ms(); 212 | mavlink_packets = p; 213 | } 214 | 215 | 216 | /* 217 | save last instance of each COMMAND_ACK 218 | */ 219 | static void command_ack_save(const mavlink_command_ack_t *m) 220 | { 221 | struct mavlink_command_ack *p; 222 | for (p=command_acks; p; p=p->next) { 223 | if (p->command == m->command) { 224 | p->result = m->result; 225 | p->receive_ms = get_time_boot_ms(); 226 | return; 227 | } 228 | } 229 | p = talloc(NULL, struct mavlink_command_ack); 230 | if (p) { 231 | p->next = command_acks; 232 | p->command = m->command; 233 | p->result = m->result; 234 | p->receive_ms = get_time_boot_ms(); 235 | command_acks = p; 236 | } 237 | } 238 | 239 | /* 240 | give last command ack result as json 241 | */ 242 | bool command_ack_get(uint16_t command, uint8_t *result, uint32_t *receive_ms) 243 | { 244 | struct mavlink_command_ack *p; 245 | for (p=command_acks; p; p=p->next) { 246 | if (p->command == command) { 247 | *result = p->result; 248 | *receive_ms = p->receive_ms; 249 | return true; 250 | } 251 | } 252 | return false; 253 | } 254 | 255 | /* 256 | get last message of a specified type 257 | */ 258 | const mavlink_message_t *mavlink_get_message_by_msgid(uint32_t msgid, uint32_t *receive_ms) 259 | { 260 | struct mavlink_packet *p; 261 | for (p=mavlink_packets; p; p=p->next) { 262 | if (p->msg.msgid == msgid) { 263 | *receive_ms = p->receive_ms; 264 | return &p->msg; 265 | } 266 | } 267 | return NULL; 268 | } 269 | 270 | /* 271 | get last message of a specified type 272 | */ 273 | const mavlink_message_t *mavlink_get_message_by_name(const char *name, uint32_t *receive_ms) 274 | { 275 | struct mavlink_packet *p; 276 | for (p=mavlink_packets; p; p=p->next) { 277 | if (p->name && strcmp(name, p->name) == 0) { 278 | *receive_ms = p->receive_ms; 279 | return &p->msg; 280 | } 281 | } 282 | return NULL; 283 | } 284 | 285 | /* 286 | get list of available mavlink packets as JSON 287 | */ 288 | void mavlink_message_list_json(struct sock_buf *sock) 289 | { 290 | sock_printf(sock, "["); 291 | bool first = true; 292 | struct mavlink_packet *p; 293 | for (p=mavlink_packets; p; p=p->next) { 294 | sock_printf(sock, "%s\"%s\"", first?"":", ", p->name); 295 | first = false; 296 | } 297 | sock_printf(sock, "]"); 298 | } 299 | 300 | /* 301 | * handle an (as yet undecoded) mavlink message 302 | */ 303 | bool mavlink_handle_msg(const mavlink_message_t *msg) 304 | { 305 | mavlink_save_packet(msg); 306 | 307 | switch(msg->msgid) { 308 | /* 309 | special handling for some messages 310 | */ 311 | case MAVLINK_MSG_ID_HEARTBEAT: { 312 | mavlink_heartbeat_t m; 313 | mavlink_msg_heartbeat_decode(msg, &m); 314 | mavlink_periodic(); 315 | break; 316 | } 317 | 318 | case MAVLINK_MSG_ID_COMMAND_ACK: { 319 | mavlink_command_ack_t m; 320 | mavlink_msg_command_ack_decode(msg, &m); 321 | command_ack_save(&m); 322 | break; 323 | } 324 | 325 | default: 326 | break; 327 | } 328 | return false; 329 | } 330 | 331 | 332 | /* 333 | set a parameter 334 | */ 335 | void mavlink_param_set(const char *name, float value) 336 | { 337 | console_printf("Setting parameter %s to %f\n", name, value); 338 | mavlink_msg_param_set_send(MAVLINK_COMM_FC, MAVLINK_TARGET_SYSTEM_ID, 0, name, value, 0); 339 | } 340 | -------------------------------------------------------------------------------- /linux/mavlink_linux.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../mavlink_core.h" 4 | 5 | struct sock_buf; 6 | 7 | const mavlink_message_t *mavlink_get_message_by_msgid(uint32_t msgid, uint32_t *receive_ms); 8 | const mavlink_message_t *mavlink_get_message_by_name(const char *name, uint32_t *receive_ms); 9 | void mavlink_message_list_json(struct sock_buf *sock); 10 | bool command_ack_get(uint16_t command, uint8_t *result, uint32_t *receive_ms); 11 | void mavlink_param_set(const char *name, float value); 12 | bool mavlink_param_get(const char *name, float *value); 13 | void mavlink_param_list_json(struct sock_buf *sock, const char *prefix, bool *first); 14 | void mavlink_fc_send(mavlink_message_t *msg); 15 | bool mavlink_handle_msg(const mavlink_message_t *msg); 16 | 17 | -------------------------------------------------------------------------------- /linux/util_linux.c: -------------------------------------------------------------------------------- 1 | /* 2 | support functions when running on Linux. Many of these are stubs for now 3 | */ 4 | #include "../includes.h" 5 | 6 | 7 | /* 8 | get upload progress as a percentage 9 | */ 10 | uint8_t get_upload_progress(void) 11 | { 12 | return 0; 13 | } 14 | 15 | /* 16 | get upload progress message 17 | */ 18 | const char *get_upload_message(void) 19 | { 20 | return ""; 21 | } 22 | 23 | static struct { 24 | bool initialised; 25 | struct timeval tv; 26 | } system_time; 27 | 28 | // get number of seconds since boot 29 | long long get_sys_seconds_boot() 30 | { 31 | if (!system_time.initialised) { 32 | gettimeofday(&system_time.tv,NULL); 33 | } 34 | struct timeval tv; 35 | gettimeofday(&tv,NULL); 36 | return tv.tv_sec - system_time.tv.tv_sec; 37 | } 38 | 39 | // get number of milliseconds since boot 40 | uint32_t get_time_boot_ms() 41 | { 42 | struct timespec elapsed_from_boot; 43 | 44 | clock_gettime(CLOCK_BOOTTIME, &elapsed_from_boot); 45 | 46 | return elapsed_from_boot.tv_sec*1000 + elapsed_from_boot.tv_nsec/1000000; 47 | } 48 | 49 | void mdelay(uint32_t ms) 50 | { 51 | uint32_t start = get_time_boot_ms(); 52 | while (get_time_boot_ms() - start < ms) { 53 | struct timeval tv; 54 | tv.tv_sec = 0; 55 | tv.tv_usec = 1000; 56 | select(0,NULL,NULL, NULL, &tv); 57 | } 58 | } 59 | 60 | bool toggle_recording(void) 61 | { 62 | printf("toggle_recording not implemented\n"); 63 | return false; 64 | } 65 | 66 | void __reboot(void) 67 | { 68 | #if __APPLE__ 69 | printf("reboot OSX implemented, but disabled for now.\n"); 70 | //reboot(0); 71 | #elif __linux__ 72 | system("/sbin/shutdown -t now -r"); 73 | #else 74 | printf("reboot not implemented\n"); 75 | #endif 76 | } 77 | 78 | 79 | /* compatibility with the sonix */ 80 | char *print_vprintf(void *ctx, const char *fmt, va_list ap) 81 | { 82 | char *ret = talloc_vasprintf(ctx, fmt, ap); 83 | if (ret) { 84 | size_t size = talloc_get_size(ret); 85 | if (size > 0 && ret[size-1]==0) { 86 | ret = talloc_realloc_size(ctx, ret, size-1); 87 | } 88 | } 89 | return ret; 90 | } 91 | 92 | void *print_printf(void *ctx, const char *fmt, ...) 93 | { 94 | va_list ap; 95 | va_start(ap, fmt); 96 | void *ret = print_vprintf(ctx, fmt, ap); 97 | va_end(ap); 98 | return ret; 99 | } 100 | 101 | 102 | 103 | unsigned xPortGetFreeHeapSize() 104 | { 105 | return 0; 106 | } 107 | -------------------------------------------------------------------------------- /linux/util_linux.h: -------------------------------------------------------------------------------- 1 | /* 2 | utility functions for Linux port 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | // get upload progress as a percentage 11 | uint8_t get_upload_progress(void); 12 | const char *get_upload_message(void); 13 | 14 | // get number of seconds since boot 15 | long long get_sys_seconds_boot(); 16 | 17 | // get number of milliseconds since boot 18 | uint32_t get_time_boot_ms(); 19 | 20 | void mdelay(uint32_t ms); 21 | 22 | bool toggle_recording(void); 23 | 24 | void __reboot(void); // __ so we don't call the os version of this by mistake. 25 | 26 | char *print_vprintf(void *ctx, const char *fmt, va_list ap); 27 | void *print_printf(void *ctx, const char *fmt, ...); 28 | 29 | 30 | 31 | unsigned mavlink_fc_pkt_count(); 32 | unsigned xPortGetFreeHeapSize(); 33 | -------------------------------------------------------------------------------- /mavlink_core.c: -------------------------------------------------------------------------------- 1 | /* 2 | core mavlink functions. This file includes mavlink_helpers.h, 3 | providing the core mavlink functions used by other modules 4 | */ 5 | #include "mavlink_core.h" 6 | #include "web_server.h" 7 | 8 | mavlink_system_t mavlink_system = {MAVLINK_SYSTEM_ID,MAVLINK_COMPONENT_ID_REMOTE_LOG}; 9 | 10 | /* 11 | send a buffer out a MAVLink channel 12 | */ 13 | void comm_send_buffer(mavlink_channel_t chan, const uint8_t *buf, uint8_t len) 14 | { 15 | switch (chan) { 16 | case MAVLINK_COMM_FC: 17 | mavlink_fc_write(buf, len); 18 | break; 19 | default: 20 | break; 21 | } 22 | } 23 | 24 | /* 25 | send one byte out a mavlink channel 26 | */ 27 | void comm_send_ch(mavlink_channel_t chan, uint8_t ch) 28 | { 29 | comm_send_buffer(chan, &ch, 1); 30 | } 31 | 32 | #include "generated/mavlink/mavlink_helpers.h" 33 | -------------------------------------------------------------------------------- /mavlink_core.h: -------------------------------------------------------------------------------- 1 | /* 2 | core mavlink functionality 3 | */ 4 | #pragma once 5 | 6 | #define MAVLINK_SEPARATE_HELPERS 7 | #define MAVLINK_NO_CONVERSION_HELPERS 8 | 9 | // arm processor does not handle misaligned accesses 10 | #define MAVLINK_ALIGNED_FIELDS 0 11 | 12 | #define MAVLINK_SEND_UART_BYTES(chan, buf, len) comm_send_buffer(chan, buf, len) 13 | 14 | // allow two mavlink ports 15 | #define MAVLINK_COMM_NUM_BUFFERS 1 16 | 17 | #include "generated/mavlink/ardupilotmega/version.h" 18 | #include "generated/mavlink/mavlink_types.h" 19 | 20 | extern mavlink_system_t mavlink_system; 21 | 22 | /* 23 | Send a byte to the nominated MAVLink channel 24 | */ 25 | void comm_send_ch(mavlink_channel_t chan, uint8_t ch); 26 | 27 | #define MAVLINK_USE_CONVENIENCE_FUNCTIONS 28 | #include "generated/mavlink/ardupilotmega/mavlink.h" 29 | 30 | // alias for link to flight controller 31 | #define MAVLINK_COMM_FC MAVLINK_COMM_0 32 | 33 | #define MAVLINK_SYSTEM_ID 67 34 | #define MAVLINK_TARGET_SYSTEM_ID 1 35 | 36 | #define MAVLINK_COMPONENT_ID_REMOTE_LOG 72 37 | -------------------------------------------------------------------------------- /mavlink_json.c: -------------------------------------------------------------------------------- 1 | /* 2 | this is the core code for the MAVLink <-> JSON gateway. It can 3 | convert MAVLink messages to JSON, and convert string function 4 | arguments to a MAVLink packet 5 | 6 | It uses the mavlink_field_info_t meta-data generated by the 7 | pymavlink C generator 8 | */ 9 | 10 | #define MAVLINK_USE_MESSAGE_INFO 11 | #include "includes.h" 12 | #include "web_server.h" 13 | #include "mavlink_core.h" 14 | #include "mavlink_json.h" 15 | 16 | static void print_one_field(struct sock_buf *sock, const mavlink_message_t *msg, const mavlink_field_info_t *f, int idx) 17 | { 18 | //#define PRINT_FORMAT(f, def) (f->print_format?f->print_format:def) 19 | #define PRINT_FORMAT(f, def) (def) 20 | switch (f->type) { 21 | case MAVLINK_TYPE_CHAR: 22 | sock_printf(sock, PRINT_FORMAT(f, "%c"), _MAV_RETURN_char(msg, f->wire_offset+idx*1)); 23 | break; 24 | case MAVLINK_TYPE_UINT8_T: 25 | sock_printf(sock, PRINT_FORMAT(f, "%u"), _MAV_RETURN_uint8_t(msg, f->wire_offset+idx*1)); 26 | break; 27 | case MAVLINK_TYPE_INT8_T: 28 | sock_printf(sock, PRINT_FORMAT(f, "%d"), _MAV_RETURN_int8_t(msg, f->wire_offset+idx*1)); 29 | break; 30 | case MAVLINK_TYPE_UINT16_T: 31 | sock_printf(sock, PRINT_FORMAT(f, "%u"), _MAV_RETURN_uint16_t(msg, f->wire_offset+idx*2)); 32 | break; 33 | case MAVLINK_TYPE_INT16_T: 34 | sock_printf(sock, PRINT_FORMAT(f, "%d"), _MAV_RETURN_int16_t(msg, f->wire_offset+idx*2)); 35 | break; 36 | case MAVLINK_TYPE_UINT32_T: 37 | sock_printf(sock, PRINT_FORMAT(f, "%lu"), (unsigned long)_MAV_RETURN_uint32_t(msg, f->wire_offset+idx*4)); 38 | break; 39 | case MAVLINK_TYPE_INT32_T: 40 | sock_printf(sock, PRINT_FORMAT(f, "%ld"), (long)_MAV_RETURN_int32_t(msg, f->wire_offset+idx*4)); 41 | break; 42 | case MAVLINK_TYPE_UINT64_T: 43 | sock_printf(sock, PRINT_FORMAT(f, "%llu"), (unsigned long long)_MAV_RETURN_uint64_t(msg, f->wire_offset+idx*8)); 44 | break; 45 | case MAVLINK_TYPE_INT64_T: 46 | sock_printf(sock, PRINT_FORMAT(f, "%lld"), (long long)_MAV_RETURN_int64_t(msg, f->wire_offset+idx*8)); 47 | break; 48 | case MAVLINK_TYPE_FLOAT: 49 | sock_printf(sock, PRINT_FORMAT(f, "%f"), (double)_MAV_RETURN_float(msg, f->wire_offset+idx*4)); 50 | break; 51 | case MAVLINK_TYPE_DOUBLE: 52 | sock_printf(sock, PRINT_FORMAT(f, "%f"), _MAV_RETURN_double(msg, f->wire_offset+idx*8)); 53 | break; 54 | } 55 | } 56 | 57 | static void print_field(struct sock_buf *sock, const mavlink_message_t *msg, const mavlink_field_info_t *f) 58 | { 59 | sock_printf(sock, "\"%s\": ", f->name); 60 | if (f->array_length == 0) { 61 | print_one_field(sock, msg, f, 0); 62 | sock_printf(sock, " "); 63 | } else { 64 | unsigned i; 65 | /* print an array */ 66 | if (f->type == MAVLINK_TYPE_CHAR) { 67 | char *str = talloc_strndup(sock, f->wire_offset+(const char *)_MAV_PAYLOAD(msg), f->array_length); 68 | sock_printf(sock, "\"%s\"", str); 69 | talloc_free(str); 70 | } else { 71 | sock_printf(sock, "[ "); 72 | for (i=0; iarray_length; i++) { 73 | print_one_field(sock, msg, f, i); 74 | if (i < f->array_length-1) { 75 | sock_printf(sock, ", "); 76 | } 77 | } 78 | sock_printf(sock, "]"); 79 | } 80 | } 81 | sock_printf(sock, " "); 82 | } 83 | 84 | /* 85 | print a JSON string for a message to the given socket 86 | */ 87 | bool mavlink_json_message(struct sock_buf *sock, const mavlink_message_t *msg, uint32_t receive_ms) 88 | { 89 | const mavlink_message_info_t *m = mavlink_get_message_info(msg); 90 | if (m == NULL) { 91 | return false; 92 | } 93 | const mavlink_field_info_t *f = m->fields; 94 | unsigned i; 95 | sock_printf(sock, "\"%s\" : { ", m->name); 96 | for (i=0; inum_fields; i++) { 97 | print_field(sock, msg, &f[i]); 98 | sock_printf(sock, ","); 99 | } 100 | sock_printf(sock, "\"_seq\" : %u, ", msg->seq); 101 | sock_printf(sock, "\"_sysid\" : %u, ", msg->sysid); 102 | sock_printf(sock, "\"_compid\" : %u, ", msg->compid); 103 | sock_printf(sock, "\"_age\" : %u", get_time_boot_ms() - receive_ms); 104 | sock_printf(sock, "}"); 105 | return true; 106 | } 107 | 108 | /* 109 | print a JSON string for a message to the given socket 110 | */ 111 | const char *mavlink_message_name(const mavlink_message_t *msg) 112 | { 113 | const mavlink_message_info_t *m = mavlink_get_message_info(msg); 114 | if (m) { 115 | return m->name; 116 | } 117 | return NULL; 118 | } 119 | 120 | /* 121 | send a mavlink message using string arguments 122 | */ 123 | bool mavlink_message_send_args(int argc, char **argv) 124 | { 125 | if (argc < 1) { 126 | return false; 127 | } 128 | const char *msg_name = argv[0]; 129 | const mavlink_message_info_t *m = mavlink_get_message_info_by_name(msg_name); 130 | if (m == NULL) { 131 | console_printf("Invalid message '%s'\n", msg_name); 132 | return false; 133 | } 134 | if (argc > m->num_fields+1) { 135 | console_printf("Invalid number of fields %u for %s\n", argc-1, msg_name); 136 | return false; 137 | } 138 | 139 | // pack the message 140 | mavlink_message_t *msg = talloc_zero(NULL, mavlink_message_t); 141 | char *buf = _MAV_PAYLOAD_NON_CONST(msg); 142 | uint8_t i; 143 | 144 | msg->msgid = m->msgid; 145 | 146 | for (i=1; i<=argc; i++) { 147 | const mavlink_field_info_t *f = &m->fields[i-1]; 148 | const char *arg = argv[i]; 149 | if (arg == NULL) { 150 | continue; 151 | } 152 | 153 | switch (f->type) { 154 | case MAVLINK_TYPE_CHAR: 155 | if (f->array_length > 0) { 156 | _mav_put_char_array(buf, f->wire_offset, arg, f->array_length); 157 | } else { 158 | _mav_put_char(buf, f->wire_offset, arg[0]); 159 | } 160 | break; 161 | 162 | case MAVLINK_TYPE_UINT8_T: { 163 | uint8_t b = simple_strtoul(arg, NULL, 0); 164 | _mav_put_uint8_t(buf, f->wire_offset, b); 165 | break; 166 | } 167 | 168 | case MAVLINK_TYPE_UINT16_T: { 169 | uint16_t b = simple_strtoul(arg, NULL, 0); 170 | _mav_put_uint16_t(buf, f->wire_offset, b); 171 | break; 172 | } 173 | 174 | case MAVLINK_TYPE_UINT32_T: { 175 | uint32_t b = simple_strtoul(arg, NULL, 0); 176 | _mav_put_uint32_t(buf, f->wire_offset, b); 177 | break; 178 | } 179 | 180 | case MAVLINK_TYPE_UINT64_T: { 181 | uint64_t b = simple_strtoul(arg, NULL, 0); 182 | _mav_put_uint64_t(buf, f->wire_offset, b); 183 | break; 184 | } 185 | 186 | case MAVLINK_TYPE_INT8_T: { 187 | int8_t b = simple_strtol(arg, NULL, 0); 188 | _mav_put_int8_t(buf, f->wire_offset, b); 189 | break; 190 | } 191 | 192 | case MAVLINK_TYPE_INT16_T: { 193 | int16_t b = strtol(arg, NULL, 0); 194 | _mav_put_int16_t(buf, f->wire_offset, b); 195 | break; 196 | } 197 | 198 | case MAVLINK_TYPE_INT32_T: { 199 | int32_t b = strtol(arg, NULL, 0); 200 | _mav_put_int32_t(buf, f->wire_offset, b); 201 | break; 202 | } 203 | 204 | case MAVLINK_TYPE_INT64_T: { 205 | int64_t b = strtol(arg, NULL, 0); 206 | _mav_put_int64_t(buf, f->wire_offset, b); 207 | break; 208 | } 209 | 210 | case MAVLINK_TYPE_FLOAT: { 211 | float b = atof(arg); 212 | _mav_put_float(buf, f->wire_offset, b); 213 | break; 214 | } 215 | 216 | case MAVLINK_TYPE_DOUBLE: { 217 | double b = atof(arg); 218 | _mav_put_double(buf, f->wire_offset, b); 219 | break; 220 | } 221 | } 222 | } 223 | 224 | uint8_t msglen = 0; 225 | for (i=0; inum_fields; i++) { 226 | const mavlink_field_info_t *f = &m->fields[i]; 227 | uint8_t len = 0; 228 | switch (f->type) { 229 | case MAVLINK_TYPE_CHAR: 230 | case MAVLINK_TYPE_UINT8_T: 231 | case MAVLINK_TYPE_INT8_T: 232 | if (f->array_length > 0) { 233 | len = f->array_length; 234 | } else { 235 | len = 1; 236 | } 237 | break; 238 | 239 | case MAVLINK_TYPE_INT16_T: 240 | case MAVLINK_TYPE_UINT16_T: 241 | len = 2; 242 | break; 243 | 244 | case MAVLINK_TYPE_INT32_T: 245 | case MAVLINK_TYPE_UINT32_T: 246 | len = 4; 247 | break; 248 | 249 | case MAVLINK_TYPE_INT64_T: 250 | case MAVLINK_TYPE_UINT64_T: 251 | len = 8; 252 | break; 253 | 254 | case MAVLINK_TYPE_FLOAT: 255 | len = 4; 256 | break; 257 | 258 | case MAVLINK_TYPE_DOUBLE: 259 | len = 8; 260 | break; 261 | } 262 | if (len + f->wire_offset > msglen) { 263 | msglen = len + f->wire_offset; 264 | } 265 | } 266 | 267 | // send as MAVLink2 268 | extern void mavlink_set_proto_version(uint8_t chan, unsigned int version); 269 | extern uint8_t mavlink_get_crc_extra(const mavlink_message_t *msg); 270 | 271 | mavlink_set_proto_version(MAVLINK_COMM_FC, 2); 272 | 273 | mavlink_finalize_message_chan(msg, mavlink_system.sysid, mavlink_system.compid, 274 | MAVLINK_COMM_FC, 275 | msglen, msglen, 276 | mavlink_get_crc_extra(msg)); 277 | 278 | mavlink_fc_send(msg); 279 | 280 | return true; 281 | } 282 | -------------------------------------------------------------------------------- /mavlink_json.h: -------------------------------------------------------------------------------- 1 | #include "mavlink_core.h" 2 | 3 | /* 4 | print a JSON string for a message to the given socket 5 | */ 6 | bool mavlink_json_message(struct sock_buf *sock, const mavlink_message_t *msg, uint32_t receive_ms); 7 | const char *mavlink_message_name(const mavlink_message_t *msg); 8 | bool mavlink_message_send_args(int argc, char **argv); 9 | -------------------------------------------------------------------------------- /posix/functions.c: -------------------------------------------------------------------------------- 1 | #include "../includes.h" 2 | #include "../template.h" 3 | #include "functions.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | static void sock_send_diskinfo(struct sock_buf *sock, const char *label, unsigned serial, unsigned total_clusters, unsigned free_clusters, unsigned cluster_size) 16 | { 17 | sock_printf(sock, 18 | "{" 19 | "\"label\" : \"%s\", " 20 | "\"serial\" : %u, " 21 | "\"total_clusters\" : %u, " 22 | "\"free_clusters\" : %u, " 23 | "\"cluster_size\" : %u" 24 | "}", 25 | label, serial, total_clusters, free_clusters, cluster_size*512); 26 | } 27 | 28 | static void disk_info(struct template_state *tmpl, const char *name, const char *value, int argc, char **argv) 29 | { 30 | unsigned long free_clusters = 0, total_clusters = 0, cluster_size = 0; 31 | const char label[] = "/"; 32 | 33 | /* Get volume information and free clusters of root filesystem */ 34 | struct statvfs buf; 35 | if (statvfs("/", &buf) != -1) { 36 | total_clusters = buf.f_blocks; 37 | free_clusters = buf.f_bfree; 38 | cluster_size = buf.f_bsize / 512; 39 | } 40 | 41 | const unsigned serial = 0; 42 | 43 | sock_send_diskinfo(tmpl->sock, label, serial, total_clusters, free_clusters, cluster_size); 44 | } 45 | 46 | 47 | static void sock_send_dirent(struct sock_buf *sock, bool first, int type, const char *name, unsigned year, unsigned month, unsigned day, unsigned hour, unsigned minute, unsigned second, unsigned size) 48 | { 49 | if (!first) { 50 | sock_printf(sock, "%s", ",\n"); 51 | } 52 | sock_printf(sock, "{" 53 | "\"type\" : %u, " 54 | "\"name\" : \"%s\", " 55 | "\"date\" : \"%04u-%02u-%02u %02u:%02u:%02u\", " 56 | "\"size\" : %u " 57 | "}", 58 | type, name, year, month, day, hour, minute, second, size); 59 | } 60 | 61 | static void file_listdir(struct template_state *tmpl, const char *name, const char *value, int argc, char **argv) 62 | { 63 | if (argc < 1) { 64 | return; 65 | } 66 | const char *dirpath = argv[0]; 67 | 68 | DIR *dh = opendir(dirpath); 69 | if (dh == NULL) { 70 | console_printf("Failed to open directory %s\n", dirpath); 71 | return; 72 | } 73 | 74 | sock_printf(tmpl->sock, "[ "); 75 | 76 | bool first = true; 77 | struct dirent *result; 78 | while ((result = readdir(dh)) != NULL) { 79 | if (strcmp(result->d_name, ".") == 0) { 80 | continue; 81 | } 82 | 83 | struct stat buf; 84 | char filepath[PATH_MAX] = {}; 85 | snprintf(filepath, sizeof(filepath), "%s/%s", dirpath, result->d_name); 86 | if (stat(filepath, &buf) == -1) { 87 | continue; 88 | } 89 | 90 | struct tm t; 91 | if (localtime_r(&buf.st_mtim.tv_sec, &t) == NULL) { 92 | continue; 93 | } 94 | 95 | sock_send_dirent(tmpl->sock, 96 | first, 97 | (result->d_type==DT_DIR) ? 1 :0, 98 | result->d_name, 99 | t.tm_year+1900, 100 | t.tm_mon+1, 101 | t.tm_mday, 102 | t.tm_hour, 103 | t.tm_min, 104 | t.tm_sec, 105 | buf.st_size); 106 | first = false; 107 | } 108 | sock_printf(tmpl->sock, "]"); 109 | closedir(dh); 110 | } 111 | 112 | void download_filesystem(struct cgi_state *cgi, const char *fs_path) 113 | { 114 | const char *path = fs_path+2; 115 | 116 | struct stat stats; 117 | if (stat(path, &stats) == -1) { 118 | cgi->http_error(cgi, "404 Bad File", "", "file not found"); 119 | return; 120 | } 121 | 122 | cgi->content_length = stats.st_size; 123 | const int fd = open(path, O_RDONLY); 124 | if(fd == -1) { 125 | cgi->http_error(cgi, "500 Open failed", "", strerror(errno)); 126 | return; 127 | } 128 | cgi->http_header(cgi, fs_path); 129 | char buf[2048]; 130 | do { 131 | const ssize_t read_count = read(fd, buf, sizeof(buf)); 132 | if (read_count == -1) { 133 | console_printf("Read failure: %s", strerror(errno)); 134 | return; 135 | } 136 | if (read_count == 0) { 137 | // EOF 138 | break; 139 | } 140 | ssize_t to_write = read_count; 141 | while (to_write > 0) { 142 | ssize_t write_count = sock_write(cgi->sock, buf, to_write); 143 | if (write_count == 0) { 144 | console_printf("EOF on write?!"); 145 | return; 146 | } 147 | if (write_count == -1) { 148 | console_printf("Error on write: %s", strerror(errno)); 149 | return; 150 | } 151 | to_write -= write_count; 152 | } 153 | } while (1); 154 | 155 | close(fd); 156 | } 157 | 158 | void posix_functions_init(struct template_state *tmpl) 159 | { 160 | tmpl->put(tmpl, "file_listdir", "", file_listdir); 161 | tmpl->put(tmpl, "disk_info", "", disk_info); 162 | } 163 | -------------------------------------------------------------------------------- /posix/functions.h: -------------------------------------------------------------------------------- 1 | /* 2 | provide server side functions in C 3 | */ 4 | 5 | #include "../template.h" 6 | #include "../includes.h" 7 | 8 | void posix_functions_init(struct template_state *tmpl); 9 | void download_filesystem(struct cgi_state *cgi, const char *fs_path); 10 | -------------------------------------------------------------------------------- /template.c: -------------------------------------------------------------------------------- 1 | /* 2 | some simple html template routines 3 | Copyright (C) Andrew Tridgell 2001 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 | */ 19 | 20 | #include "includes.h" 21 | #include "functions.h" 22 | 23 | static void process_tag(struct template_state *tmpl, const char *tag); 24 | 25 | /* 26 | fetch a variable from the template variables list 27 | */ 28 | static struct template_var *find_var(struct template_state *tmpl, const char *name) 29 | { 30 | struct template_var *var; 31 | 32 | for (var = tmpl->variables; var; var = var->next) { 33 | if (strcmp(var->name, name) == 0) { 34 | return var; 35 | } 36 | } 37 | return NULL; 38 | } 39 | 40 | /* 41 | add a name/value pair to the list of template variables 42 | */ 43 | static void put(struct template_state *tmpl, const char *name, 44 | const char *value, template_fn fn) 45 | { 46 | struct template_var *var; 47 | if (!name || !value) return; 48 | 49 | var = find_var(tmpl, name); 50 | if (var) { 51 | talloc_free(var->value); 52 | } else { 53 | var = talloc(tmpl, struct template_var); 54 | var->next = tmpl->variables; 55 | tmpl->variables = var; 56 | var->name = talloc_strdup(var, name); 57 | var->function = fn; 58 | } 59 | var->value = talloc_strdup(var, value); 60 | web_debug(5, "put(%s,%s)\n", name, value); 61 | } 62 | 63 | /* fetch a variable from the template variables list */ 64 | static const char *get(struct template_state *tmpl, const char *name) 65 | { 66 | struct template_var *var; 67 | 68 | var = find_var(tmpl, name); 69 | if (var) return var->value; 70 | 71 | return NULL; 72 | } 73 | 74 | 75 | /* process a template variable */ 76 | static void process_variable(struct template_state *tmpl, const char *tag) 77 | { 78 | const char *v = tmpl->get(tmpl, tag); 79 | if (v) { 80 | sock_printf(tmpl->sock, "%s", v); 81 | } 82 | } 83 | 84 | /* process setting a template variable */ 85 | static void process_set_var(struct template_state *tmpl, char *tag) 86 | { 87 | char *p; 88 | p = strchr(tag, '='); 89 | if (!p) return; 90 | *p++ = 0; 91 | trim_tail(tag, " \t"); 92 | trim_tail(p, " \t"); 93 | while (isspace(*p)) p++; 94 | tmpl->put(tmpl, tag, p, NULL); 95 | } 96 | 97 | /* process a template variable with quote escaping */ 98 | static void process_escaped_variable(struct template_state *tmpl, const char *tag) 99 | { 100 | const char *v = tmpl->get(tmpl, tag); 101 | while (v && *v) { 102 | if (*v == '"') { 103 | sock_printf(tmpl->sock, """); 104 | } else { 105 | sock_write(tmpl->sock, v, 1); 106 | } 107 | v++; 108 | } 109 | } 110 | 111 | /* process a call into a C function setup with put_function() */ 112 | static void process_c_call(struct template_state *tmpl, const char *tag) 113 | { 114 | struct template_var *var; 115 | char *name, *args, *p, *tok; 116 | char **argv; 117 | int argc=0; 118 | 119 | if (!(p=strchr(tag, '('))) return; 120 | 121 | web_debug(2, "process_c_call: %s\n", tag); 122 | 123 | name = talloc_strndup(tmpl, tag, strcspn(tag, "(")); 124 | 125 | var = find_var(tmpl, name); 126 | if (!var || !var->function) { 127 | web_debug(4,"No function '%s'\n", name); 128 | talloc_free(name); 129 | return; 130 | } 131 | 132 | args = talloc_strndup(tmpl, p+1, strcspn(p+1, ")")); 133 | 134 | argv = talloc(tmpl, char *); 135 | for (tok = strtok_r(args, ",", &p); tok; tok = strtok_r(NULL, ",", &p)) { 136 | argv = talloc_realloc_size(tmpl, argv, (argc+2)*sizeof(char *)); 137 | while (isspace(*tok)) tok++; 138 | trim_tail(tok, " \t\r\n"); 139 | argv[argc++] = tok; 140 | } 141 | argv[argc] = NULL; 142 | 143 | var->function(tmpl, name, var->value, argc, argv); 144 | talloc_free(args); 145 | talloc_free(name); 146 | } 147 | 148 | /* 149 | process a single tag 150 | */ 151 | static void process_tag(struct template_state *tmpl, const char *tag) 152 | { 153 | char *tag2, *p; 154 | int recurse = 1; 155 | 156 | while (isspace(*tag)) tag++; 157 | 158 | tag2 = talloc_strdup(tmpl, tag); 159 | trim_tail(tag2, " \t\n\r"); 160 | 161 | p = tag2; 162 | 163 | if (*p == '-') { 164 | p++; 165 | recurse = 0; 166 | } 167 | 168 | switch (*p) { 169 | case '$': 170 | process_variable(tmpl, p+1); 171 | break; 172 | case '%': 173 | process_escaped_variable(tmpl, p+1); 174 | break; 175 | case '@': 176 | process_c_call(tmpl, p+1); 177 | break; 178 | case '#': 179 | /* its a comment, ignore it */ 180 | break; 181 | default: 182 | if (strchr(tag2, '=')) { 183 | process_set_var(tmpl, p); 184 | } else { 185 | /* an include file */ 186 | tmpl->process(tmpl, p, recurse); 187 | } 188 | } 189 | talloc_free(tag2); 190 | } 191 | 192 | /* 193 | process provided content 194 | */ 195 | static int process_content(struct template_state *tmpl, const char *mp, uint32_t size) 196 | { 197 | size_t remaining; 198 | const char *m; 199 | char *p, *s; 200 | int recurse = 1; 201 | 202 | remaining = size; 203 | m = mp; 204 | 205 | if (strncmp(m, "#!", 2) == 0) { 206 | /* advance past shell script tag */ 207 | m = strchr(m, '\n'); 208 | if (!m) return 0; 209 | m++; 210 | remaining -= (m-mp); 211 | } 212 | 213 | /* tags look like {{ TAG }} 214 | where TAG can be of several forms 215 | */ 216 | while (recurse && remaining && (p = strstr(m, START_TAG))) { 217 | const char *m0 = m; 218 | int len; 219 | char *contents, *s2; 220 | const char *m2; 221 | int depth=1; 222 | 223 | sock_write(tmpl->sock, m, (p-m)); 224 | m = p + strlen(START_TAG); 225 | m2 = m; 226 | while (depth) { 227 | s2 = strstr(m2, START_TAG); 228 | s = strstr(m2, END_TAG); 229 | if (!s) break; 230 | if (s2 && s2 < s) { 231 | depth++; 232 | m2 = s2 + strlen(START_TAG); 233 | } else { 234 | depth--; 235 | m2 = s + strlen(END_TAG); 236 | } 237 | } 238 | if (!s || depth) { 239 | console_printf("No termination of tag!\n"); 240 | return -1; 241 | } 242 | len = (s-m); 243 | while (len && isspace(m[len-1])) len--; 244 | contents = talloc_strndup(tmpl, m, len); 245 | process_tag(tmpl, contents); 246 | talloc_free(contents); 247 | m = s + strlen(END_TAG); 248 | remaining -= (m - m0); 249 | } 250 | 251 | if (remaining > 0) { 252 | sock_write(tmpl->sock, m, remaining); 253 | } 254 | return 0; 255 | } 256 | 257 | /* 258 | process a template file 259 | */ 260 | static int process(struct template_state *tmpl, const char *filename, int recurse) 261 | { 262 | size_t size; 263 | const char *mp; 264 | 265 | mp = get_embedded_file(filename, &size); 266 | if (!mp) { 267 | console_printf("Failed to map %s\n", filename); 268 | return -1; 269 | } 270 | 271 | return process_content(tmpl, mp, size); 272 | } 273 | 274 | 275 | static struct template_state template_base = { 276 | /* methods */ 277 | process, 278 | put, 279 | get, 280 | process_c_call, 281 | process_content, 282 | 283 | /* rest are zero */ 284 | NULL, 285 | NULL, 286 | }; 287 | 288 | struct template_state *template_init(void *ctx, struct sock_buf *sock) 289 | { 290 | struct template_state *tmpl; 291 | 292 | tmpl = talloc(ctx, struct template_state); 293 | if (tmpl) { 294 | memcpy(tmpl, &template_base, sizeof(*tmpl)); 295 | tmpl->sock = sock; 296 | } 297 | 298 | functions_init(tmpl); 299 | 300 | return tmpl; 301 | } 302 | -------------------------------------------------------------------------------- /template.h: -------------------------------------------------------------------------------- 1 | /* 2 | some simple http template routines 3 | Copyright (C) Andrew Tridgell 2002 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 | */ 19 | 20 | #pragma once 21 | 22 | typedef void (*template_fn)(struct template_state *, const char *, const char *, 23 | int, char **); 24 | 25 | struct template_var { 26 | struct template_var *next; 27 | char *name; 28 | char *value; 29 | template_fn function; 30 | }; 31 | 32 | 33 | struct template_state { 34 | /* methods */ 35 | int (*process)(struct template_state *, const char *, int); 36 | void (*put)(struct template_state *, const char *, const char *, template_fn fn); 37 | const char *(*get)(struct template_state *, const char *name); 38 | void (*process_c_call)(struct template_state *, const char *); 39 | int (*process_content)(struct template_state *tmpl, const char *mp, uint32_t size); 40 | 41 | /* data */ 42 | struct template_var *variables; 43 | struct sock_buf *sock; 44 | }; 45 | 46 | #define START_TAG "{{" 47 | #define END_TAG "}}" 48 | 49 | /* prototypes */ 50 | struct template_state *template_init(void *ctx, struct sock_buf *sock); 51 | -------------------------------------------------------------------------------- /web_files.c: -------------------------------------------------------------------------------- 1 | /* 2 | support for embedded files. 3 | 4 | files are embedded as C arrays in the web server to support running 5 | on systems which don't have a local filesystem 6 | 7 | See files/embed.py for the creation of the embedded arrays 8 | */ 9 | 10 | #include "includes.h" 11 | 12 | #include "files/embedded.c" 13 | 14 | /* 15 | return pointer to embedded file, or NULL 16 | */ 17 | const char *get_embedded_file(const char *filename, size_t *size) 18 | { 19 | uint16_t i; 20 | for (i=0; embedded_files[i].filename; i++) { 21 | if (strcmp(filename, embedded_files[i].filename) == 0) { 22 | *size = embedded_files[i].size; 23 | return embedded_files[i].contents; 24 | } 25 | } 26 | return NULL; 27 | } 28 | -------------------------------------------------------------------------------- /web_files.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | support for embedded files 5 | */ 6 | 7 | 8 | /* 9 | return pointer to embedded file, or NULL 10 | */ 11 | const char *get_embedded_file(const char *filename, size_t *size); 12 | 13 | -------------------------------------------------------------------------------- /web_server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef SYSTEM_FREERTOS 4 | #include 5 | #include 6 | #include 7 | #include "../dev_console.h" 8 | #include "../mavlink_wifi.h" 9 | #else 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #endif 38 | 39 | struct connection_state; 40 | 41 | #include "cgi.h" 42 | 43 | #define WEB_SERVER_PORT 80 44 | 45 | /* 46 | structure for output buffering 47 | */ 48 | struct sock_buf { 49 | bool add_content_length; 50 | uint32_t header_length; 51 | char *buf; 52 | int fd; 53 | }; 54 | 55 | 56 | /* 57 | state of one connection 58 | */ 59 | struct connection_state { 60 | #ifdef SYSTEM_FREERTOS 61 | xTaskHandle task; 62 | #endif 63 | struct sock_buf *sock; 64 | struct cgi_state *cgi; 65 | }; 66 | 67 | void web_server_task_process(void *pvParameters); 68 | void connection_destroy(struct connection_state *c); 69 | #ifdef SYSTEM_FREERTOS 70 | int32_t sock_write(struct sock_buf *sock, const char *s, size_t size); 71 | #else 72 | ssize_t sock_write(struct sock_buf *sock, const char *s, size_t size); 73 | #endif 74 | #ifndef SYSTEM_FREERTOS 75 | #define FMT_PRINTF(a,b) __attribute__((format(printf, a, b))) 76 | #endif 77 | void sock_printf(struct sock_buf *sock, const char *fmt, ...) FMT_PRINTF(2,3); 78 | void web_server_set_debug(int debug); 79 | void web_debug(int level, const char *fmt, ...); 80 | void mavlink_fc_write(const uint8_t *buf, size_t len); 81 | #ifdef SYSTEM_FREERTOS 82 | void mavlink_rc_write(const uint8_t *buf, uint32_t len); 83 | #endif 84 | 85 | 86 | #ifndef SYSTEM_FREERTOS 87 | #define console_printf printf 88 | #define console_vprintf vprintf 89 | 90 | #define simple_strtoul strtoul 91 | #define simple_strtol strtol 92 | #endif 93 | --------------------------------------------------------------------------------