/* * This file is part of the chronometer project. * Copyright 2019 Edward V. Emelianov . * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "adc.h" #include "GPS.h" #include "lidar.h" #include "str.h" #include "time.h" #include "usart.h" #include "usb.h" // flag to show new GPS message over USB uint8_t showGPSstr = 0; extern uint32_t shotms[]; /** * @brief cmpstr - the same as strncmp * @param s1,s2 - strings to compare * @param n - max symbols amount * @return 0 if strings equal or 1/-1 */ int cmpstr(const char *s1, const char *s2, int n){ int ret = 0; while(--n){ ret = *s1 - *s2; if(ret == 0 && *s1 && *s2){ ++s1; ++s2; continue; } break; } return ret; } /** * @brief getchr - analog of strchr * @param str - string to search * @param symbol - searching symbol * @return pointer to symbol found or NULL */ char *getchr(const char *str, char symbol){ do{ if(*str == symbol) return (char*)str; }while(*(++str)); return NULL; } #define sendu(x) do{USB_send(u2str(x));}while(0) static void sendi(int32_t I){ if(I < 0){ USB_send("-"); I = -I; } USB_send(u2str((uint32_t)I)); } /** * @brief showuserconf - show configuration over USB */ static void showuserconf(){ USB_send("DISTMIN="); sendu(the_conf.dist_min); USB_send("\nDISTMAX="); sendu(the_conf.dist_max); USB_send("\nADCMIN="); sendi(the_conf.ADC_min); USB_send("\nADCMAX="); sendi(the_conf.ADC_max); USB_send("\nTRIGLVL="); sendu(the_conf.trigstate); USB_send("\nTRIGPAUSE={"); for(int i = 0; i < TRIGGERS_AMOUNT; ++i){ if(i) USB_send(", "); sendu(the_conf.trigpause[i]); } USB_send("}"); USB_send("\nUSART1SPD="); sendu(the_conf.USART_speed); USB_send("\nSTREND="); if(the_conf.defflags & FLAG_STRENDRN) USB_send("RN"); else USB_send("N"); uint8_t f = the_conf.defflags; USB_send("\nSAVE_EVENTS="); if(f & FLAG_SAVE_EVENTS) USB_send("1"); else USB_send("0"); USB_send("\nGPSPROXY="); if(f & FLAG_GPSPROXY) USB_send("1"); else USB_send("0"); USB_send("\nNFREE="); sendu(the_conf.NLfreeWarn); USB_send("\n"); } /** * @brief parse_USBCMD - parsing of string buffer got by USB * @param cmd - buffer with commands * @return 0 if got command, 1 if command not recognized */ int parse_USBCMD(char *cmd){ #define CMP(a,b) cmpstr(a, b, sizeof(b)) #define GETNUM(x) if(getnum(cmd+sizeof(x)-1, &N)) goto bad_number; static uint8_t conf_modified = 0; uint8_t succeed = 0; int32_t N; if(!cmd || !*cmd) return 0; IWDG->KR = IWDG_REFRESH; if(*cmd == '?'){ // help USB_send("Commands:\n" CMD_ADCMAX " - max ADC value treshold for trigger\n" CMD_ADCMIN " - min -//- (triggered when ADval>min & 0xffff) goto bad_number; if(the_conf.dist_min != (uint16_t)N){ conf_modified = 1; the_conf.dist_min = (uint16_t) N; } succeed = 1; }else if(CMP(cmd, CMD_DISTMAX) == 0){ // set low limit DBG("CMD_DISTMAX"); GETNUM(CMD_DISTMAX); if(N < 0 || N > 0xffff) goto bad_number; if(the_conf.dist_max != (uint16_t)N){ conf_modified = 1; the_conf.dist_max = (uint16_t) N; } succeed = 1; }else if(CMP(cmd, CMD_STORECONF) == 0){ // store everything DBG("Store"); if(conf_modified){ if(store_userconf()){ USB_send("Error: can't save data!\n"); }else{ conf_modified = 0; succeed = 1; } } }else if(CMP(cmd, CMD_GPSSTR) == 0){ // show GPS status string showGPSstr = 1; }else if(CMP(cmd, CMD_TRIGLVL) == 0){ DBG("Trig levels"); cmd += sizeof(CMD_TRIGLVL) - 1; uint8_t Nt = *cmd++ - '0'; if(Nt > TRIGGERS_AMOUNT - 1) goto bad_number; uint8_t state = *cmd -'0'; if(state > 1) goto bad_number; uint8_t oldval = the_conf.trigstate; if(!state) the_conf.trigstate = oldval & ~(1< TRIGGERS_AMOUNT - 1) goto bad_number; if(getnum(cmd, &N)) goto bad_number; if(N < 0 || N > 10000) goto bad_number; if(the_conf.trigpause[Nt] != N){ conf_modified = 1; the_conf.trigpause[Nt] = N; } succeed = 1; }else if(CMP(cmd, CMD_TRGTIME) == 0){ DBG("Trigger time"); cmd += sizeof(CMD_TRGTIME) - 1; uint8_t Nt = *cmd++ - '0'; if(Nt > TRIGGERS_AMOUNT - 1) goto bad_number; show_trigger_shot((uint8_t)1< 1) goto bad_number; USB_send("LEDS="); if(Nt){ LEDSon = 1; USB_send("ON\n"); }else{ LED_off(); // turn off LEDS LED1_off(); // by user request LEDSon = 0; USB_send("OFF\n"); } }else if(CMP(cmd, CMD_ADCMAX) == 0){ // set low limit GETNUM(CMD_ADCMAX); if(N < -4096 || N > 4096) goto bad_number; if(the_conf.ADC_max != (int16_t)N){ conf_modified = 1; the_conf.ADC_max = (int16_t) N; } succeed = 1; }else if(CMP(cmd, CMD_ADCMIN) == 0){ // set low limit GETNUM(CMD_ADCMIN); if(N < -4096 || N > 4096) goto bad_number; if(the_conf.ADC_min != (int16_t)N){ conf_modified = 1; the_conf.ADC_min = (int16_t) N; } succeed = 1; }else if(CMP(cmd, CMD_GPSRESTART) == 0){ USB_send("Send full cold restart to GPS\n"); GPS_send_FullColdStart(); }else if(CMP(cmd, CMD_BUZZER) == 0){ uint8_t Nt = cmd[sizeof(CMD_BUZZER) - 1] - '0'; if(Nt > 1) goto bad_number; USB_send("BUZZER="); if(Nt){ buzzer_on = 1; USB_send("ON\n"); }else{ buzzer_on = 0; USB_send("OFF\n"); } }else if(CMP(cmd, CMD_GPSSTAT) == 0){ USB_send("GPS status: "); const char *str = "unknown"; switch(GPS_status){ case GPS_NOTFOUND: str = "not found"; break; case GPS_WAIT: str = "waiting"; break; case GPS_NOT_VALID: str = "no satellites"; break; case GPS_VALID: str = "valid time"; break; } USB_send(str); if(Tms - last_corr_time < 1500) USB_send(", PPS working\n"); else USB_send(", no PPS\n"); }else if(CMP(cmd, CMD_USARTSPD) == 0){ GETNUM(CMD_USARTSPD); if(N < 400 || N > 3000000) goto bad_number; if(the_conf.USART_speed != (uint32_t)N){ the_conf.USART_speed = (uint32_t)N; conf_modified = 1; } succeed = 1; }else if(CMP(cmd, CMD_RESET) == 0){ USB_send("Soft reset\n"); NVIC_SystemReset(); }else if(CMP(cmd, CMD_STREND) == 0){ char c = cmd[sizeof(CMD_STREND) - 1]; succeed = 1; if(c == 'n' || c == 'N'){ if(the_conf.defflags & FLAG_STRENDRN){ conf_modified = 1; the_conf.defflags &= ~FLAG_STRENDRN; } }else if(c == 'r' || c == 'R'){ if(!(the_conf.defflags & FLAG_STRENDRN)){ conf_modified = 1; the_conf.defflags |= FLAG_STRENDRN; } }else{ succeed = 0; USB_send("Bad letter, should be 'n' or 'r'\n"); } }else if(CMP(cmd, CMD_FLASH) == 0){ // show flash size USB_send("FLASHSIZE="); sendu(FLASH_SIZE); USB_send("kB\nFLASH_BASE="); USB_send(u2hex(FLASH_BASE)); USB_send("\nFlash_Data="); USB_send(u2hex((uint32_t)Flash_Data)); USB_send("\nvarslen="); sendu((uint32_t)&_varslen); USB_send("\nCONFsize="); sendu(sizeof(user_conf)); USB_send("\nNconf_records="); sendu(maxCnum - 1); USB_send("\nlogsstart="); USB_send(u2hex((uint32_t)logsstart)); USB_send("\nLOGsize="); sendu(sizeof(event_log)); USB_send("\nNlogs_records="); sendu(maxLnum - 1); USB_send("\n"); }else if(CMP(cmd, CMD_SAVEEVTS) == 0){ if('0' == cmd[sizeof(CMD_SAVEEVTS) - 1]){ if(the_conf.defflags & FLAG_SAVE_EVENTS){ conf_modified = 1; the_conf.defflags &= ~FLAG_SAVE_EVENTS; } }else{ if(!(the_conf.defflags & FLAG_SAVE_EVENTS)){ conf_modified = 1; the_conf.defflags |= FLAG_SAVE_EVENTS; } } succeed = 1; }else if(CMP(cmd, CMD_DUMP) == 0){ if(getnum(cmd+sizeof(CMD_DUMP)-1, &N)) N = -20; // default - without N else N = -N; if(N > 0) N = 0; if(dump_log(N, -1)) USB_send("Event log empty!\n"); }else if(CMP(cmd, CMD_NFREE) == 0){ GETNUM(CMD_NFREE); if(N < 0 || N > 0xffff) goto bad_number; if(the_conf.NLfreeWarn != (uint16_t)N){ conf_modified = 1; the_conf.NLfreeWarn = (uint16_t)N; } succeed = 1; }else if(CMP(cmd, CMD_DELLOGS) == 0){ if(store_log(NULL)) USB_send("Error during erasing flash\n"); else USB_send("All logs erased\n"); }else if(CMP(cmd, CMD_GPSPROXY) == 0){ if(cmd[sizeof(CMD_GPSPROXY) - 1] == '0'){ if(the_conf.defflags & FLAG_GPSPROXY){ conf_modified = 1; the_conf.defflags &= ~FLAG_GPSPROXY; } }else{ if(!(the_conf.defflags & FLAG_GPSPROXY)){ conf_modified = 1; the_conf.defflags |= FLAG_GPSPROXY; } } succeed = 1; }else if(CMP(cmd, CMD_CURDIST) == 0){ USB_send("DIST="); sendu(last_lidar_dist); USB_send("\nSTREN="); sendu(last_lidar_stren); USB_send("\nTRIGDIST="); sendu(lidar_triggered_dist); USB_send("\nTms="); sendu(Tms); USB_send("\nshotms="); sendu(shotms[LIDAR_TRIGGER]); USB_send("\n"); }else return 1; /*else if(CMP(cmd, CMD_) == 0){ ; }*/ IWDG->KR = IWDG_REFRESH; if(succeed) USB_send("Success!\n"); return 0; bad_number: USB_send("Error: bad number!\n"); return 0; } /** * @brief get_trigger_shot - print on USB message about last trigger shot time * @param number - number of event (if > -1) * @param logdata - record from event log * @return string with data */ char *get_trigger_shot(int number, const event_log *logdata){ static char buf[64]; char *bptr = buf; if(number > -1){ bptr = strcp(bptr, u2str(number)); bptr = strcp(bptr, ": "); } if(logdata->trigno == LIDAR_TRIGGER){ bptr = strcp(bptr, "LIDAR, dist="); bptr = strcp(bptr, u2str(logdata->lidar_dist)); bptr = strcp(bptr, ", TRIG" STR(LIDAR_TRIGGER)); }else{ bptr = strcp(bptr, "TRIG"); *bptr++ = '0' + logdata->trigno; } *bptr++ = '='; IWDG->KR = IWDG_REFRESH; bptr = strcp(bptr, get_time(&logdata->shottime.Time, logdata->shottime.millis)); bptr = strcp(bptr, ", len="); if(logdata->triglen < 0) bptr = strcp(bptr, ">1s"); else bptr = strcp(bptr, u2str((uint32_t) logdata->triglen)); *bptr++ = '\n'; *bptr++ = 0; return buf; } /** * @brief show_trigger_shot printout @ USB data with all triggers shot recently (+ save it in flash) * @param tshot - each bit consists information about trigger */ void show_trigger_shot(uint8_t tshot){ uint8_t X = 1; for(int i = 0; i < TRIGGERS_AMOUNT && tshot; ++i, X <<= 1){ IWDG->KR = IWDG_REFRESH; if(tshot & X) tshot &= ~X; else continue; event_log l; l.elog_sz = sizeof(event_log); l.trigno = i; if(i == LIDAR_TRIGGER) l.lidar_dist = lidar_triggered_dist; l.shottime = shottime[i]; l.triglen = triglen[i]; USB_send(get_trigger_shot(-1, &l)); if(the_conf.defflags & FLAG_SAVE_EVENTS){ if(store_log(&l)) USB_send("\n\nError saving event!\n\n"); } } } /** * @brief strln == strlen * @param s - string * @return length */ int strln(const char *s){ int i = 0; while(*s++) ++i; return i; } /** * @brief strcp - strcpy (be carefull: it doesn't checks destination length!) * @param dst - destination * @param src - source * @return pointer to '\0' @ dst`s end */ char *strcp(char* dst, const char *src){ int l = strln(src); if(l < 1) return dst; while((*dst++ = *src++)); return dst - 1; } // read `buf` and get first integer `N` in it // @return 0 if all OK or 1 if there's not a number; omit spaces and '=' int getnum(const char *buf, int32_t *N){ char c; int positive = -1; int32_t val = 0; while((c = *buf++)){ if(c == '\t' || c == ' ' || c == '='){ if(positive < 0) continue; // beginning spaces else break; // spaces after number } if(c == '-'){ if(positive < 0){ positive = 0; continue; }else break; // there already was `-` or number } if(c < '0' || c > '9') break; if(positive < 0) positive = 1; val = val * 10 + (int32_t)(c - '0'); } if(positive != -1){ if(positive == 0){ if(val == 0) return 1; // single '-' val = -val; } *N = val; }else return 1; return 0; } static char strbuf[11]; // return string buffer (strbuf) with val char *u2str(uint32_t val){ char *bufptr = &strbuf[10]; *bufptr = 0; if(!val){ *(--bufptr) = '0'; }else{ while(val){ *(--bufptr) = val % 10 + '0'; val /= 10; } } return bufptr; } // return strbuf filled with hex char *u2hex(uint32_t val){ char *bufptr = strbuf; *bufptr++ = '0'; *bufptr++ = 'x'; uint8_t *ptr = (uint8_t*)&val + 3; int i, j; IWDG->KR = IWDG_REFRESH; for(i = 0; i < 4; ++i, --ptr){ for(j = 1; j > -1; --j){ register uint8_t half = (*ptr >> (4*j)) & 0x0f; if(half < 10) *bufptr++ = half + '0'; else *bufptr++ = half - 10 + 'a'; } } *bufptr = 0; return strbuf; }