示例#1
0
void RaceAnalyzerComm::ReadRuntime(RuntimeValues &values){
	wxMutexLocker lock(_commMutex);
	try{
		wxDateTime start = wxDateTime::UNow();
		CComm *serialPort = GetSerialPort();
		if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

		wxString rsp = SendCommand(serialPort, "sample");

		wxStringTokenizer tokenizer(rsp,";");
		while (tokenizer.HasMoreTokens()){
			wxString token = tokenizer.GetNextToken();
			wxStringTokenizer subTokenizer(token,"=");
			if (subTokenizer.CountTokens() >= 2){
				wxString name = subTokenizer.GetNextToken();
				wxString value = subTokenizer.GetNextToken();
				UnhideInnerTokens(value);
				if (value.StartsWith("\"") && value.EndsWith("\"")){
					value = value.Left(value.Len() - 1);
					value = value.Right(value.Len() - 1);
				}
				values[name] = atof(value);
			}
		}
	}
	catch(CommException &e){
		CloseSerialPort();
		throw e;
	}
}
示例#2
0
wxString RaceAnalyzerComm::readScript(){

	wxMutexLocker lock(_commMutex);

	wxString script = "";
	int scriptPage = 0;
	int to = 0;
	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	FlushReceiveBuffer(serialPort);
	while(!to){

		//wxString cmd = wxString::Format("println(getScriptPage(%d))",scriptPage++);
		wxString cmd = wxString::Format("readScriptPage %d",scriptPage++);
		wxString buffer = SendCommand(serialPort, cmd);

		wxString scriptFrag = GetParam(buffer,"script", true);

		VERBOSE(FMT("escaped: %s", scriptFrag.ToAscii()));
		Unescape(scriptFrag);
		VERBOSE(FMT("unescaped: %s", scriptFrag.ToAscii()));
		size_t scriptFragmentLen = scriptFrag.Length();

		if (scriptFragmentLen > 0 ) script+=scriptFrag;
		//the last page is a 'partial page'
		if (scriptFragmentLen < SCRIPT_PAGE_LENGTH ) break;
	}
	CloseSerialPort();
	if (to){
		throw CommException(CommException::COMM_TIMEOUT);
	}
	return script;
}
示例#3
0
void RaceAnalyzerComm::reloadScript(void){
	wxMutexLocker lock(_commMutex);

	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	wxString reloadCmd = "reloadScript";
	wxString result = SendCommand(serialPort, reloadCmd);
	CheckThrowResult(result);
}
示例#4
0
void	CCOMSET::ScanPort()
{
	CStringArray	arrayComm;
	int i=0;
	for (;i<GetSerialPort(arrayComm);i++)
	{
		((CComboBox*)GetDlgItem(IDC_COMBO_COMSET))->AddString(arrayComm[i]);
	}
 	((CComboBox*)GetDlgItem(IDC_COMBO_COMSET))->SetCurSel(i-1);
}
示例#5
0
void RaceAnalyzerComm::readGpioConfig(GpioConfig *config){
	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	for (int i = 0; i < CONFIG_GPIO_CHANNELS; i++){
		GpioConfig &gpioConfig = (config[i]);
		wxString cmd = wxString::Format("getGpioCfg %d",i);
		wxString rsp = SendCommand(serialPort,cmd);
		populateChannelConfig(gpioConfig.channelConfig,rsp);
		gpioConfig.mode = (gpio_mode_t)GetIntParam(rsp,"mode");
	}
}
示例#6
0
void RaceAnalyzerComm::readGpsConfig(GpsConfig *config){
	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	wxString cmd = "getGpsCfg";
	wxString rsp = SendCommand(serialPort,cmd);
	config->gpsInstalled = GetIntParam(rsp,"installed") != 0;
	populateChannelConfig(config->latitudeCfg,"lat",rsp);
	populateChannelConfig(config->longitudeCfg,"long",rsp);
	populateChannelConfig(config->speedCfg,"vel",rsp);
	populateChannelConfig(config->timeCfg,"time",rsp);
	populateChannelConfig(config->satellitesCfg,"sats",rsp);
}
示例#7
0
void RaceAnalyzerComm::readConfig(RaceCaptureConfig *config,RaceAnalyzerCommCallback *callback){
	try{
		wxDateTime start = wxDateTime::UNow();
		int updateCount = 0;
		CComm *serialPort = GetSerialPort();
		if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

		updateWriteConfigPct(updateCount, callback);

		readGpsConfig(&config->gpsConfig);
		updateWriteConfigPct(++updateCount, callback);

		readGpsTargetConfig(&config->gpsConfig);
		updateWriteConfigPct(++updateCount, callback);

		readAnalogConfig(config->analogConfigs);
		updateCount += CONFIG_ANALOG_CHANNELS;
		updateWriteConfigPct(updateCount, callback);

		readTimerConfig(config->timerConfigs);
		updateCount += CONFIG_TIMER_CHANNELS;
		updateWriteConfigPct(updateCount, callback);

		readAccelConfig(config->accelConfigs);
		updateCount += CONFIG_ACCEL_CHANNELS;
		updateWriteConfigPct(updateCount, callback);

		readAnalogPulseConfig(config->pwmConfigs);
		updateCount += CONFIG_ANALOG_PULSE_CHANNELS;
		updateWriteConfigPct(updateCount, callback);

		readGpioConfig(config->gpioConfigs);
		updateCount += CONFIG_GPIO_CHANNELS;
		updateWriteConfigPct(updateCount, callback);

		readConnectivityConfig(&config->connectivityConfig);
		updateWriteConfigPct(++updateCount,callback);

		config->luaScript = readScript();
		updateWriteConfigPct(++updateCount,callback);

		wxTimeSpan dur = wxDateTime::UNow() - start;
		VERBOSE(FMT("get config in %f",dur.GetMilliseconds().ToDouble()));
		callback->ReadConfigComplete(true,"");
	}
	catch(CommException &e){
		callback->ReadConfigComplete(false, e.GetErrorMessage());
	}
	CloseSerialPort();
}
示例#8
0
void RaceAnalyzerComm::readAccelConfig(AccelConfig *config){
	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	for (int i = 0; i < CONFIG_ACCEL_CHANNELS; i++){
		AccelConfig &accelConfig = (config[i]);
		wxString cmd = wxString::Format("getAccelCfg %d",i);
		wxString rsp = SendCommand(serialPort,cmd);
		populateChannelConfig(accelConfig.channelConfig,rsp);
		accelConfig.mode = (accel_mode_t)GetIntParam(rsp,"mode");
		accelConfig.channel = (accel_channel_t)GetIntParam(rsp,"channel");
		accelConfig.zeroValue = GetIntParam(rsp,"zeroValue");
	}
}
示例#9
0
void RaceAnalyzerComm::readGpsTargetConfig(GpsConfig *config){
	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	wxString cmd = "getStartFinishCfg";
	wxString rsp = SendCommand(serialPort,cmd);
	populateChannelConfig(config->lapCountCfg,"lapCount",rsp);
	populateChannelConfig(config->lapTimeCfg,"lapTime",rsp);
	populateChannelConfig(config->splitTimeCfg, "splitTime", rsp);
	config->startFinishTarget.latitude = GetFloatParam(rsp,"startFinishLat");
	config->startFinishTarget.longitude = GetFloatParam(rsp,"startFinishLong");
	config->startFinishTarget.targetRadius = GetFloatParam(rsp,"startFinishRadius");
	config->splitTarget.latitude = GetFloatParam(rsp, "splitLat");
	config->splitTarget.longitude = GetFloatParam(rsp, "splitLong");
	config->splitTarget.targetRadius = GetFloatParam(rsp, "splitRadius");
}
示例#10
0
void RaceAnalyzerComm::flashCurrentConfig(){
	try{
		wxDateTime start = wxDateTime::UNow();
		CComm *serialPort = GetSerialPort();
		if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);
		wxString cmd = "flashLoggerCfg";

		wxString result = SendCommand(serialPort, cmd);
		CheckThrowResult(result);
		wxTimeSpan dur = wxDateTime::UNow() - start;
		VERBOSE(FMT("flash config in %f",dur.GetMilliseconds().ToDouble()));
	}
	catch(CommException &e){
		VERBOSE(FMT("Error during flash: %s", e.GetErrorMessage()));
	}
	CloseSerialPort();
}
示例#11
0
void RaceAnalyzerComm::readAnalogPulseConfig(PwmConfig *config){
	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	for (int i = 0; i < CONFIG_ANALOG_PULSE_CHANNELS; i++){
		PwmConfig &pwmConfig = (config[i]);
		wxString cmd = wxString::Format("getPwmCfg %d",i);
		wxString rsp = SendCommand(serialPort,cmd);
		populateChannelConfig(pwmConfig.channelConfig,rsp);
		pwmConfig.loggingPrecision = GetIntParam(rsp,"loggingPrecision");
		pwmConfig.outputMode = (pwm_output_mode_t)GetIntParam(rsp,"outputMode");
		pwmConfig.loggingMode = (pwm_logging_mode_t)GetIntParam(rsp,"loggingMode");
		pwmConfig.startupDutyCycle = GetIntParam(rsp,"startupDutyCycle");
		pwmConfig.startupPeriod = GetIntParam(rsp,"startupPeriod");
		pwmConfig.voltageScaling = GetFloatParam(rsp,"voltageScaling");
	}
}
示例#12
0
void RaceAnalyzerComm::readTimerConfig(TimerConfig *config){
	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	for (int i = 0; i < CONFIG_TIMER_CHANNELS; i++){
		TimerConfig &timerConfig = config[i];
		wxString cmd = wxString::Format("getTimerCfg %d",i);
		wxString rsp = SendCommand(serialPort,cmd);
		populateChannelConfig(timerConfig.channelConfig,rsp);
		timerConfig.loggingPrecision = GetIntParam(rsp,"loggingPrecision");
		timerConfig.slowTimerEnabled = GetIntParam(rsp,"slowTimer") == 1;
		timerConfig.mode = (timer_mode_t)GetIntParam(rsp,"mode");
		timerConfig.pulsePerRev = GetIntParam(rsp,"pulsePerRev");
		timerConfig.timerDivider = GetIntParam(rsp,"divider");
		timerConfig.scaling = GetIntParam(rsp,"scaling");
	}
}
示例#13
0
void RaceAnalyzerComm::readConnectivityConfig(ConnectivityConfig *config){
	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	wxString cmd = "getOutputCfg";
	wxString rsp = SendCommand(serialPort,cmd);
	config->sdLoggingMode = (sd_logging_mode_t)GetIntParam(rsp,"sdLoggingMode");
	config->connectivityMode = (connectivity_mode_t)GetIntParam(rsp,"telemetryMode");
	config->p2pConfig.destinationAddrHigh = GetIntParam(rsp,"p2pDestAddrHigh");
	config->p2pConfig.destinationAddrLow = GetIntParam(rsp,"p2pDestAddrLow");
	config->telemetryConfig.telemetryServer = GetParam(rsp, "telemetryServerHost");
	config->telemetryConfig.telemetryDeviceId = GetParam(rsp, "telemetryDeviceId");

	rsp = SendCommand(serialPort, "getCellCfg");
	config->cellularConfig.apnHost = GetParam(rsp, "apnHost");
	config->cellularConfig.apnUser = GetParam(rsp, "apnUser");
	config->cellularConfig.apnPass = GetParam(rsp, "apnPass");
}
示例#14
0
void RaceAnalyzerComm::calibrateAccelZero(){
	wxMutexLocker lock(_commMutex);
	try{
		wxDateTime start = wxDateTime::UNow();
		CComm *serialPort = GetSerialPort();
		if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);
		wxString cmd = "calibrateAccelZero";

		wxString result = SendCommand(serialPort, cmd);
		CheckThrowResult(result);
		wxTimeSpan dur = wxDateTime::UNow() - start;
		VERBOSE(FMT("calibrateAccelZero in %f",dur.GetMilliseconds().ToDouble()));
	}
	catch(CommException &e){
		VERBOSE(FMT("Error during calibrateAccelZero: %s", e.GetErrorMessage()));
		throw e;
	}
	CloseSerialPort();
}
示例#15
0
void RaceAnalyzerComm::readAnalogConfig(AnalogConfig *config){
	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	for (int i = 0; i < CONFIG_ANALOG_CHANNELS; i++){
		AnalogConfig &analogConfig = (config[i]);
		wxString cmd = wxString::Format("getAnalogCfg %d",i);
		wxString rsp = SendCommand(serialPort,cmd);
		populateChannelConfig(analogConfig.channelConfig,rsp);
		analogConfig.loggingPrecision = GetIntParam(rsp,"loggingPrecision");
		analogConfig.scalingMode = (scaling_mode_t)GetIntParam(rsp,"scalingMode");
		analogConfig.linearScaling = GetFloatParam(rsp,"scaling");
		for (int ii=0; ii < CONFIG_ANALOG_SCALING_BINS;ii++){
			analogConfig.scalingMap.rawValues[ii]=GetIntParam(rsp,wxString::Format("mapRaw_%d",ii));
		}
		for (int ii=0; ii < CONFIG_ANALOG_SCALING_BINS;ii++){
			analogConfig.scalingMap.scaledValue[ii]=GetFloatParam(rsp,wxString::Format("mapScaled_%d",ii));
		}
	}
}
示例#16
0
void RaceAnalyzerComm::writeScript(wxString &script){

	wxMutexLocker lock(_commMutex);
	size_t index = 0;
	int page,to;
	page = 0;
	to = 0;
	size_t length = script.Length();

	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	while(index < length && page < SCRIPT_PAGES && !to){
		wxString scriptFragment;
		if (index + SCRIPT_PAGE_LENGTH > length){
			scriptFragment = script.Mid(index);
		}else{
			scriptFragment = script.Mid(index, SCRIPT_PAGE_LENGTH);
		}
		Escape(scriptFragment);
		//wxString cmd = wxString::Format("updateScriptPage(%d,\"%s\")", page,data.ToAscii());
		wxString cmd = wxString::Format("writeScriptPage %d %s",page,scriptFragment.ToAscii());
		wxString result = SendCommand(serialPort, cmd);
		CheckThrowResult(result);
		page++;
		index += SCRIPT_PAGE_LENGTH;
	}
	//did we write fewer than the max number of script pages?
	//note we're subtracting script pages by one to account for integer divide truncation
	if ((length / SCRIPT_PAGE_LENGTH) < SCRIPT_PAGES - 1 ){
		//write a null to the next page
		wxString cmd = wxString::Format("writeScriptPage %d",page);
		wxString result = SendCommand(serialPort, cmd);
		CheckThrowResult(result);
	}

	CloseSerialPort();
	if (to){
		throw CommException(CommException::COMM_TIMEOUT);
	}
}
示例#17
0
void RaceAnalyzerComm::ReadVersion(VersionData &version){
	wxMutexLocker lock(_commMutex);
	CComm *serialPort = GetSerialPort();
	if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

	wxString result = SendCommand(serialPort, "version");

	long major = 0;
	long minor = 0;
	long bugfix = 0;

	if (	GetParam(result, "major").ToLong(&major,10) &&
			GetParam(result, "minor").ToLong(&minor, 10) &&
			GetParam(result, "bugfix").ToLong(&bugfix, 10)){

	}
	else{
		throw CommException(CommException::CMD_ERROR,wxString::Format("Could not read version response: %s", result));
	}

	version.SetMajor(major);
	version.SetMinor(minor);
	version.SetBugfix(bugfix);
}
示例#18
0
wxString RaceAnalyzerComm::GetLogfile(){
	wxMutexLocker lock(_commMutex);
	wxString logfileData;
	try{
		CComm *serialPort = GetSerialPort();
		if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);
		wxString rsp = SendCommand(serialPort, "{\"getLogfile\":0}",1000);
		try{
			if (rsp.Len() > 0 ){
				Object root = ParseJSON(rsp);
				const String &val = root["logfile"];
				logfileData = val.Value();
				}
			}
			catch (const Exception& e){
				ERROR(FMT("could not parse logfile response: %s",rsp));
			}
	}
	catch(CommException &e){
		CloseSerialPort();
		throw e;
	}
	return logfileData;
}
void main( /*int argc, char *argv[]*/ )
{

    int s_pressed = 0;
    char c1[] = {"COM6"};
    COMMTIMEOUTS timeouts = {0};
    pthread_t idp,idc,id_aes,idr;
    pthread_t id_ana;

    pthread_t id_aes_rec,id_melp;
    pthread_t play;
    pthread_t id_decryptor;

    pthread_t id_melp_1ana, id_melp_2ana, id_melp_3ana;

    sem_init(&mutx, NULL, 1);
    sem_init(&empty, NULL, 1);
    sem_init(&full, NULL, 0);
    sem_init(&last, NULL, 0);
    sem_init(&wait_read, NULL, 0);
    sem_init(&wait_melp, NULL, 0);
    sem_init(&rec_start, NULL, 1);
    sem_init(&id_analyzer, NULL, 0);
    /*==================receiver semaphores======================*/
    sem_init(&melp_start, NULL, 0);
    sem_init(&play_start, NULL, 0);
    sem_init(&reciever_start, NULL, 0);
    sem_init(&decryptor_start, NULL, 0);

    sem_init(&melp_1ana, NULL, 0);
    sem_init(&melp_2ana, NULL, 0);
    sem_init(&melp_3ana, NULL, 0);

    sem_init(&start_melp_1ana, NULL, 0);
    sem_init(&start_melp_2ana, NULL, 0);
    sem_init(&start_melp_3ana, NULL, 0);


    h1 = GetSerialPort(c1);
    timeouts.ReadIntervalTimeout = 100;
    timeouts.ReadTotalTimeoutConstant = 100;
    timeouts.ReadTotalTimeoutMultiplier = 20;
    if(!SetCommTimeouts(h1, &timeouts)) {
        printf("Error setting port state \n");
    }

    printf("TX\n\n");
    printf("Press S to Start\n\n");
    printf("Press C to Stop\n\n");
    // Creating threads
    while(1)
    {
        //while(!kbhit());
        //printf("keystroke pressed\n");
        //key_hit = getch();
        //exit(1);
        if(kbhit())	key_hit = getch();
        if(key_hit == 's' && s_pressed == 0)
        {
            if(EscapeCommFunction(h1,SETRTS) == 0)
            {
                printf ("Error Setting RTS\n");
                exit(1);
            }
            s_pressed = 1;
            if(start == 0)
            {
                start = 1;
                pthread_create(&idr, NULL, recorder, NULL);
                //pthread_create(&idc, NULL, melp, NULL);
                pthread_create(&idp, NULL, producer, NULL);

                pthread_create(&id_melp_1ana, NULL, melp_analysis1, NULL);
                pthread_create(&id_melp_2ana, NULL, melp_analysis2, NULL);
                pthread_create(&id_melp_3ana, NULL, melp_analysis3, NULL);

                pthread_create(&id_aes, NULL, aes_serial, NULL);
                pthread_create(&id_ana, NULL, analyzer, NULL);

                pthread_create(&id_aes_rec, NULL, aes_receiver, NULL);
                pthread_create(&id_melp, NULL, melp_rec, NULL);
                pthread_create(&play, NULL, readandplay, NULL);
                pthread_create(&id_decryptor, NULL, decryptor, NULL);
            }
            else
            {
                sem_post(&rec_start);
                sem_wait(&reciever_start);
            }
        }
        else if(key_hit == 'c' && s_pressed == 1)
            //else if(key_hit == 'c')
        {
            //start = 0;
            s_pressed = 0;
            sem_wait(&rec_start);
            //last_file = 1;
            //while(flag == 0);


            sem_post(&reciever_start);
            // Waiting for threads to complete operation

        }
        else if(key_hit == 'e')// && start == 0)
        {
            break;
        }
        else
        {
            //printf("Press any valid key\n\n");
            if(s_pressed == 0)
            {
                printf("Press S to Start\n\n");
                printf("Press C to Stop\n\n");
                printf("Press C followed by E to Exit\n\n");
            }
            while(!kbhit());
        }
    }
    pthread_join(idr, NULL);
    pthread_join(idp, NULL);
    pthread_join(idc, NULL);
    pthread_join(id_aes, NULL);
    pthread_join(id_ana, NULL);

    pthread_exit(NULL);
}
示例#20
0
void RaceAnalyzerComm::writeConfig(RaceCaptureConfig *config, RaceAnalyzerCommCallback * callback){
	try{
		wxDateTime start = wxDateTime::UNow();
		CComm *serialPort = GetSerialPort();
		if (NULL==serialPort) throw CommException(CommException::OPEN_PORT_FAILED);

		int updateCount = 0;
		for (int i = 0; i < CONFIG_ANALOG_CHANNELS;i++){
			AnalogConfig &cfg = config->analogConfigs[i];
			wxString cmd = "setAnalogCfg";
			cmd = AppendIntParam(cmd, i);
			cmd = AppendChannelConfig(cmd, cfg.channelConfig);
			cmd = AppendIntParam(cmd, cfg.loggingPrecision);
			cmd = AppendIntParam(cmd, cfg.scalingMode);
			cmd = AppendFloatParam(cmd, cfg.linearScaling);
			ScalingMap &map = (cfg.scalingMap);
			for (int m=0; m < CONFIG_ANALOG_SCALING_BINS;m++){
				cmd = AppendIntParam(cmd, map.rawValues[m]);
			}
			for (int m=0; m < CONFIG_ANALOG_SCALING_BINS;m++){
				cmd = AppendFloatParam(cmd, map.scaledValue[m]);
			}
			wxString result = SendCommand(serialPort, cmd);
			CheckThrowResult(result);
			updateWriteConfigPct(++updateCount,callback);
		}
		for (int i = 0; i < CONFIG_TIMER_CHANNELS; i++){
			TimerConfig &cfg = config->timerConfigs[i];
			wxString cmd = "setTimerCfg";
			cmd = AppendIntParam(cmd, i);
			AppendChannelConfig(cmd, cfg.channelConfig);
			cmd = AppendIntParam(cmd, cfg.loggingPrecision);
			cmd = AppendIntParam(cmd, cfg.slowTimerEnabled);
			cmd = AppendIntParam(cmd, cfg.mode);
			cmd = AppendIntParam(cmd, cfg.pulsePerRev);
			cmd = AppendIntParam(cmd, cfg.timerDivider);
			cmd = AppendIntParam(cmd, cfg.scaling);
			wxString result = SendCommand(serialPort, cmd);
			CheckThrowResult(result);
			updateWriteConfigPct(++updateCount,callback);
		}
		for (int i = 0; i < CONFIG_ACCEL_CHANNELS; i++){
			AccelConfig &cfg = config->accelConfigs[i];
			wxString cmd = "setAccelCfg";
			cmd = AppendIntParam(cmd, i);
			cmd = AppendChannelConfig(cmd, cfg.channelConfig);
			cmd = AppendIntParam(cmd, cfg.mode);
			cmd = AppendIntParam(cmd, cfg.channel);
			cmd = AppendIntParam(cmd, cfg.zeroValue);
			wxString result = SendCommand(serialPort, cmd);
			CheckThrowResult(result);
			updateWriteConfigPct(++updateCount,callback);
		}
		for (int i = 0; i < CONFIG_ANALOG_PULSE_CHANNELS; i++){
			PwmConfig &cfg = config->pwmConfigs[i];
			wxString cmd = "setPwmCfg";
			cmd = AppendIntParam(cmd, i);
			cmd = AppendChannelConfig(cmd, cfg.channelConfig);
			cmd = AppendIntParam(cmd, cfg.loggingPrecision);
			cmd = AppendIntParam(cmd, cfg.outputMode);
			cmd = AppendIntParam(cmd, cfg.loggingMode);
			cmd = AppendIntParam(cmd, cfg.startupDutyCycle);
			cmd = AppendIntParam(cmd, cfg.startupPeriod);
			cmd = AppendFloatParam(cmd, cfg.voltageScaling);
			wxString result = SendCommand(serialPort, cmd);
			CheckThrowResult(result);
			updateWriteConfigPct(++updateCount,callback);
		}
		for (int i = 0; i < CONFIG_GPIO_CHANNELS; i++){
			GpioConfig &cfg = config->gpioConfigs[i];
			wxString cmd = "setGpioCfg";
			cmd = AppendIntParam(cmd,i);
			cmd = AppendChannelConfig(cmd, cfg.channelConfig);
			cmd = AppendIntParam(cmd, cfg.mode);
			wxString result = SendCommand(serialPort, cmd);
			CheckThrowResult(result);
			updateWriteConfigPct(++updateCount,callback);
		}
		{
			GpsConfig &cfg = config->gpsConfig;
			{
				wxString cmd = "setGpsCfg";
				cmd = AppendIntParam(cmd, cfg.gpsInstalled);
				cmd = AppendChannelConfig(cmd, cfg.latitudeCfg);
				cmd = AppendChannelConfig(cmd, cfg.longitudeCfg);
				cmd = AppendChannelConfig(cmd, cfg.speedCfg);
				cmd = AppendChannelConfig(cmd, cfg.timeCfg);
				cmd = AppendChannelConfig(cmd, cfg.satellitesCfg);
				wxString result = SendCommand(serialPort, cmd);
				CheckThrowResult(result);
				updateWriteConfigPct(++updateCount,callback);
			}
			{
				wxString cmd = "setStartFinishCfg";
				cmd = AppendChannelConfig(cmd, cfg.lapCountCfg);
				cmd = AppendChannelConfig(cmd, cfg.lapTimeCfg);
				cmd = AppendChannelConfig(cmd, cfg.splitTimeCfg);
				cmd = AppendFloatParam(cmd, cfg.startFinishTarget.latitude);
				cmd = AppendFloatParam(cmd, cfg.startFinishTarget.longitude);
				cmd = AppendFloatParam(cmd, cfg.startFinishTarget.targetRadius);
				cmd = AppendFloatParam(cmd, cfg.splitTarget.latitude);
				cmd = AppendFloatParam(cmd, cfg.splitTarget.longitude);
				cmd = AppendFloatParam(cmd, cfg.splitTarget.targetRadius);
				wxString result = SendCommand(serialPort, cmd);
				CheckThrowResult(result);
				updateWriteConfigPct(++updateCount,callback);
			}
		}
		{
			ConnectivityConfig &cfg = config->connectivityConfig;
			wxString cmd = "setOutputCfg";
			cmd = AppendIntParam(cmd, cfg.sdLoggingMode);
			cmd = AppendIntParam(cmd, cfg.connectivityMode);
			cmd = AppendUIntParam(cmd, cfg.p2pConfig.destinationAddrHigh);
			cmd = AppendUIntParam(cmd, cfg.p2pConfig.destinationAddrLow);
			cmd = AppendStringParam(cmd, cfg.telemetryConfig.telemetryServer);
			cmd = AppendStringParam(cmd, cfg.telemetryConfig.telemetryDeviceId);

			wxString result = SendCommand(serialPort, cmd);
			CheckThrowResult(result);

			cmd = "setCellCfg";
			cmd = AppendStringParam(cmd, cfg.cellularConfig.apnHost);
			cmd = AppendStringParam(cmd, cfg.cellularConfig.apnUser);
			cmd = AppendStringParam(cmd, cfg.cellularConfig.apnPass);
			result = SendCommand(serialPort, cmd);
			CheckThrowResult(result);

			updateWriteConfigPct(++updateCount,callback);
		}
		{
			writeScript(config->luaScript);
			updateWriteConfigPct(++updateCount,callback);
		}
		wxTimeSpan dur = wxDateTime::UNow() - start;
		VERBOSE(FMT("write config in %f",dur.GetMilliseconds().ToDouble()));
		callback->WriteConfigComplete(true,"");
	}
	catch(CommException &e){
		callback->WriteConfigComplete(false, e.GetErrorMessage());
	}
	CloseSerialPort();
}