static void sendAnalogConfig(Serial *serial, size_t startIndex, size_t endIndex){

	json_messageStart(serial);
	json_blockStart(serial, "getAnalogCfg");
	for (size_t i = startIndex; i <= endIndex; i++){

		ADCConfig *cfg = &(getWorkingLoggerConfig()->ADCConfigs[i]);
		json_blockStartInt(serial, i);
		json_channelConfig(serial, &(cfg->cfg), 1);
		json_int(serial, "scalMod", cfg->scalingMode, 1);

		json_int(serial, "prec", cfg->loggingPrecision, 1);
		json_float(serial, "linScal", cfg->linearScaling, ANALOG_SCALING_PRECISION, 1);

		json_blockStart(serial, "map");
		json_arrayStart(serial, "raw");

		for (size_t b = 0; b < ANALOG_SCALING_BINS; b++){
			put_int(serial,  cfg->scalingMap.rawValues[b]);
			if (b < ANALOG_SCALING_BINS - 1) serial->put_c(',');
		}
		json_arrayEnd(serial, 1);
		json_arrayStart(serial, "scal");
		for (size_t b = 0; b < ANALOG_SCALING_BINS; b++){
			put_float(serial, cfg->scalingMap.scaledValues[b], ANALOG_SCALING_PRECISION);
			if (b < ANALOG_SCALING_BINS - 1) serial->put_c(',');
		}
		json_arrayEnd(serial, 0);
		json_blockEnd(serial, 0); //map
		json_blockEnd(serial, i != endIndex); //index
	}
	json_blockEnd(serial, 0);
	json_blockEnd(serial, 0);
}
void OBD2Task(void *pvParameters)
{
    pr_info("Start OBD2 task\r\n");
    size_t max_obd2_samplerate = msToTicks((TICK_RATE_HZ / MAX_OBD2_SAMPLE_RATE));
    LoggerConfig *config = getWorkingLoggerConfig();
    OBD2Config *oc = &config->OBD2Configs;
    while(1) {
        while(oc->enabled && oc->enabledPids > 0) {
            for (size_t i = 0; i < oc->enabledPids; i++) {
                PidConfig *pidCfg = &oc->pids[i];
                int value;
                unsigned char pid = pidCfg->pid;
                if (OBD2_request_PID(pid, &value, OBD2_PID_DEFAULT_TIMEOUT_MS)) {
                    OBD2_set_current_PID_value(i, value);
                    if (TRACE_LEVEL) {
                        pr_trace("read OBD2 PID ");
                        pr_trace_int(pid);
                        pr_trace("=")
                        pr_trace_int(value);
                        pr_trace("\r\n");
                    }
                } else {
                    pr_warning_int_msg("OBD2: PID read fail: ", pid);
                }
                delayTicks(max_obd2_samplerate);
            }
        }
        delayMs(OBD2_FEATURE_DISABLED_DELAY_MS);
    }
}
static void lapstats_setup(const GpsSnapshot *gps_snapshot)
{
    /*
     * I am not a fan of this method.  Really this track config stuff should
     * get moved over into its own file with its own separate testing.  It
     * should generate an event when a track is detected so that we can listen
     * and act accordingly.  For now it stays here.
     */
    const LoggerConfig *config = getWorkingLoggerConfig();
    const GeoPoint *gp = &gps_snapshot->sample.point;

    const Track *track = NULL;
    const TrackConfig *trackConfig = &(config->TrackConfigs);
    if (trackConfig->auto_detect) {
        track = auto_configure_track(NULL, gp);
        if (track)
            pr_info_int_msg("track: detected track ",
                            track->trackId);
    } else {
        track = &trackConfig->track;
        pr_info("track: using fixed config");
    }

    const float gc_radius = degrees_to_meters(config->TrackConfigs.radius);
    lapstats_setup_internals(track, gc_radius, trackConfig->auto_detect);
}
int api_setTrackConfig(Serial *serial, const jsmntok_t *json){

	TrackConfig *trackCfg = &(getWorkingLoggerConfig()->TrackConfigs);
	const jsmntok_t * targetData = json + 1;

	const jsmntok_t *startFinish = findNode(targetData,"startFinish");
	if (startFinish != NULL) setTargetConfig(startFinish + 1, &trackCfg->startFinishConfig);

	const jsmntok_t *split = findNode(targetData, "split");
	if (split != NULL) setTargetConfig(split + 1, &trackCfg->splitConfig);

	const jsmntok_t *lapCount = findNode(targetData, "lapCount");
	if (lapCount != NULL) setChannelConfig(serial, lapCount + 1, &trackCfg->lapCountCfg, NULL, NULL);

	const jsmntok_t *lapTime = findNode(targetData, "lapTime");
	if (lapTime != NULL) setChannelConfig(serial, lapTime + 1, &trackCfg->lapTimeCfg, NULL, NULL);

	const jsmntok_t *splitTime = findNode(targetData, "splitTime");
	if (splitTime != NULL) setChannelConfig(serial, splitTime + 1, &trackCfg->splitTimeCfg, NULL, NULL);

	const jsmntok_t *distance = findNode(targetData, "dist");
	if (distance != NULL) setChannelConfig(serial, distance + 1, &trackCfg->distanceCfg, NULL, NULL);

	const jsmntok_t *predTime = findNode(targetData, "predTime");
	if (predTime != NULL) setChannelConfig(serial, predTime + 1, &trackCfg->predTimeCfg, NULL, NULL);

	return API_SUCCESS;
}
int api_setGpsConfig(Serial *serial, const jsmntok_t *json){

	GPSConfig *gpsCfg = &(getWorkingLoggerConfig()->GPSConfigs);
	const jsmntok_t * channelData = json + 1;

	const jsmntok_t * latitudeNode = findNode(channelData, "lat");
	if (latitudeNode != NULL) setChannelConfig(serial, latitudeNode + 1, &gpsCfg->latitudeCfg, NULL, NULL);

	const jsmntok_t * longitudeNode = findNode(channelData, "long");
	if (longitudeNode != NULL) setChannelConfig(serial, longitudeNode + 1, &gpsCfg->longitudeCfg, NULL, NULL);

	const jsmntok_t * speedNode = findNode(channelData, "speed");
	if (speedNode != NULL) setChannelConfig(serial, speedNode + 1, &gpsCfg->speedCfg, NULL, NULL);
	
	const jsmntok_t * dateNode = findNode(channelData, "date");
	if (dateNode != NULL) setChannelConfig(serial, dateNode + 1, &gpsCfg->dateCfg, NULL, NULL);

	const jsmntok_t * timeNode = findNode(channelData, "time");
	if (timeNode != NULL) setChannelConfig(serial, timeNode + 1, &gpsCfg->timeCfg, NULL, NULL);

	const jsmntok_t * satsNode = findNode(channelData, "sats");
	if (satsNode != NULL) setChannelConfig(serial, satsNode + 1, &gpsCfg->satellitesCfg, NULL, NULL);

	return API_SUCCESS;
}
int api_getTrackConfig(Serial *serial, const jsmntok_t *json){
	TrackConfig *trackCfg = &(getWorkingLoggerConfig()->TrackConfigs);

	json_messageStart(serial);
	json_blockStart(serial, "getTrackCfg");
	json_gpsTarget(serial, "startFinish", &trackCfg->startFinishConfig, 1);
	json_gpsTarget(serial, "split", &trackCfg->splitConfig, 1);
	json_blockStart(serial, "lapCount");
	json_channelConfig(serial, &trackCfg->lapCountCfg, 0);
	json_blockEnd(serial, 1);
	json_blockStart(serial, "lapTime");
	json_channelConfig(serial, &trackCfg->lapTimeCfg, 0);
	json_blockEnd(serial, 1);
	json_blockStart(serial, "splitTime");
	json_channelConfig(serial, &trackCfg->splitTimeCfg, 0);
	json_blockEnd(serial, 1);
	json_blockStart(serial, "dist");
	json_channelConfig(serial, &trackCfg->distanceCfg, 0);
	json_blockEnd(serial, 1);
	json_blockStart(serial, "predTime");
	json_channelConfig(serial, &trackCfg->predTimeCfg, 0);
	json_blockEnd(serial, 0);
	json_blockEnd(serial, 0);
	json_blockEnd(serial, 0);

	return API_SUCCESS_NO_RETURN;
}
int Lua_SetBackgroundStreaming(lua_State *L)
{
    if (lua_gettop(L) >= 1) {
        getWorkingLoggerConfig()->ConnectivityConfigs.telemetryConfig.backgroundStreaming = (lua_tointeger(L,1) == 1);
    }
    return 0;
}
int api_getGpsConfig(Serial *serial, const jsmntok_t *json){

	GPSConfig *gpsCfg = &(getWorkingLoggerConfig()->GPSConfigs);

	json_messageStart(serial);
	json_blockStart(serial, "getGpsCfg");

	json_blockStart(serial, "lat");
	json_channelConfig(serial, &gpsCfg->latitudeCfg, 0);
	json_blockEnd(serial, 1);

	json_blockStart(serial, "long");
	json_channelConfig(serial, &gpsCfg->longitudeCfg, 0);
	json_blockEnd(serial, 1);

	json_blockStart(serial, "speed");
	json_channelConfig(serial, &gpsCfg->speedCfg, 0);
	json_blockEnd(serial, 1);

	json_blockStart(serial, "sats");
	json_channelConfig(serial, &gpsCfg->satellitesCfg, 0);
	json_blockEnd(serial, 1);

	json_blockStart(serial, "date");
	json_channelConfig(serial, &gpsCfg->dateCfg, 0);
	json_blockEnd(serial, 0);

	json_blockStart(serial, "time");
	json_channelConfig(serial, &gpsCfg->timeCfg, 0);
	json_blockEnd(serial, 0);

	json_blockEnd(serial, 0);
	json_blockEnd(serial, 0);
	return API_SUCCESS_NO_RETURN;
}
TimerConfig* get_timer_config(int channel)
{
        const size_t idx = (size_t) channel;
        if (idx >= CONFIG_TIMER_CHANNELS)
                return NULL;

        return getWorkingLoggerConfig()->TimerConfigs + idx;
}
int api_getBluetoothConfig(Serial *serial, const jsmntok_t *json){
	BluetoothConfig *cfg = &(getWorkingLoggerConfig()->ConnectivityConfigs.bluetoothConfig);
	json_messageStart(serial);
	json_blockStart(serial, "getBtCfg");
	json_string(serial, "name", cfg->deviceName, 1);
	json_string(serial, "pass", cfg->passcode, 0);
	json_blockEnd(serial, 0);
	json_blockEnd(serial, 0);
	return API_SUCCESS_NO_RETURN;
}
int api_getCellConfig(Serial *serial, const jsmntok_t *json){
	CellularConfig *cfg = &(getWorkingLoggerConfig()->ConnectivityConfigs.cellularConfig);
	json_messageStart(serial);
	json_blockStart(serial, "getCellCfg");
	json_string(serial, "apnHost", cfg->apnHost, 1);
	json_string(serial, "apnUser", cfg->apnUser, 1);
	json_string(serial, "apnPass", cfg->apnPass, 0);
	json_blockEnd(serial, 0);
	json_blockEnd(serial, 0);
	return API_SUCCESS_NO_RETURN;
}
int Lua_SetPWMClockFrequency(lua_State *L)
{
    if (lua_gettop(L) >= 1) {
        LoggerConfig *loggerConfig = getWorkingLoggerConfig();

        uint16_t clock_frequency = filterPwmClockFrequency(lua_tointeger(L,1));
        loggerConfig->PWMClockFrequency = clock_frequency;
        PWM_set_clock_frequency(clock_frequency);
    }
    return 0;
}
int api_getConnectivityConfig(Serial *serial, const jsmntok_t *json){
	ConnectivityConfig *cfg = &(getWorkingLoggerConfig()->ConnectivityConfigs);
	json_messageStart(serial);
	json_blockStart(serial, "getConnCfg");
	json_int(serial, "sdMode", cfg->sdLoggingMode, 1);
	json_int(serial, "connMode", cfg->connectivityMode, 1);
	json_int(serial, "bgStream", cfg->backgroundStreaming, 0);
	json_blockEnd(serial, 0);
	json_blockEnd(serial, 0);
	return API_SUCCESS_NO_RETURN;
}
int api_getMeta(Serial *serial, const jsmntok_t *json){
	json_messageStart(serial);
	SampleRecord * sr = (SampleRecord *)portMalloc(sizeof(SampleRecord));
	if (sr == 0) return API_ERROR_SEVERE;

	initSampleRecord(getWorkingLoggerConfig(), sr);
	writeSampleMeta(serial, sr, getConnectivitySampleRateLimit(), 0);

	portFree(sr);
	json_blockEnd(serial, 0);
	return API_SUCCESS_NO_RETURN;
}
int main(int argc, char* argv[])
{

	LoggerConfig *config = getWorkingLoggerConfig();
	initApi();
	initialize_logger_config();
	setupMockSerial();
	imu_init(config);
	resetPredictiveTimer();

	int pt;

	pt = open("/dev/ptmx", O_RDWR | O_NOCTTY);
	if (pt < 0)
	{
		perror("open /dev/ptmx");
		return 1;
	}

	grantpt(pt);
	unlockpt(pt);

	fprintf(stderr, "RaceCapture/Pro simulator on: %s\n", ptsname(pt));

	char line[LINE_BUFFER_SIZE];
	memset(line, 0, LINE_BUFFER_SIZE);
	int messageCount = 0;
	while(1){
		size_t count = 0;
		line[0] = '\0';
		while (count < LINE_BUFFER_SIZE){
			char c;
			read(pt, &c, 1);
			line[count] = c;
			count++;
			if (c == '\r'){
				line[count] = '\0';
				break;
			}
		}

		printf("rx: (%zu): %s\r\n", strlen(line), line);
		mock_resetTxBuffer();
		process_api(getMockSerial(), line, strlen(line));
		char *txBuffer = mock_getTxBuffer();
		printf("tx:(%zu) %s\r\n", strlen(txBuffer), txBuffer);
		write(pt, txBuffer, strlen(txBuffer));
		pr_info_int(++messageCount);
		pr_info(" messages\r\n");
	}
	return 0;
}
static void sendGpioConfig(Serial *serial, size_t startIndex, size_t endIndex){
	json_messageStart(serial);
	json_blockStart(serial, "getGpioCfg");
	for (size_t i = startIndex; i <= endIndex; i++){
		GPIOConfig *cfg = &(getWorkingLoggerConfig()->GPIOConfigs[i]);
		json_blockStartInt(serial, i);
		json_channelConfig(serial, &(cfg->cfg), 1);
		json_uint(serial, "mode", cfg->mode, 0);
		json_blockEnd(serial, i != endIndex);
	}
	json_blockEnd(serial, 0);
	json_blockEnd(serial, 0);
}
int Lua_ReadImu(lua_State *L)
{
    if (lua_gettop(L) >= 1) {
        unsigned int channel = (unsigned int)lua_tointeger(L,1);
        if (channel >= 0 && channel < CONFIG_IMU_CHANNELS) {
            ImuConfig *ac = &getWorkingLoggerConfig()->ImuConfigs[channel];
            float value = imu_read_value(channel,ac);
            lua_pushnumber(L, value);
            return 1;
        }
    }
    return 0;
}
static void sendAccelConfig(Serial *serial, size_t startIndex, size_t endIndex){
	json_messageStart(serial);
	json_blockStart(serial, "getAccelCfg");
	for (size_t i = startIndex; i <= endIndex; i++){
		AccelConfig *cfg = &(getWorkingLoggerConfig()->AccelConfigs[i]);
		json_blockStartInt(serial, i);
		json_channelConfig(serial, &(cfg->cfg), 1);
		json_uint(serial, "mode", cfg->mode, 1);
		json_uint(serial, "chan", cfg->accelChannel, 1);
		json_uint(serial, "zeroVal", cfg->zeroValue, 0);
		json_blockEnd(serial, i != endIndex); //index
	}
	json_blockEnd(serial, 0);
	json_blockEnd(serial, 0);
}
static void sendTimerConfig(Serial *serial, size_t startIndex, size_t endIndex){
	json_messageStart(serial);
	json_blockStart(serial, "getTimerCfg");
	for (size_t i = startIndex; i <= endIndex; i++){
		TimerConfig *cfg = &(getWorkingLoggerConfig()->TimerConfigs[i]);
		json_blockStartInt(serial, i);
		json_channelConfig(serial, &(cfg->cfg), 1);
		json_uint(serial, "prec", cfg->loggingPrecision, 1);
		json_uint(serial, "sTimer", cfg->slowTimerEnabled, 1);
		json_uint(serial, "mode", cfg->mode, 1);
		json_uint(serial, "ppRev", cfg->pulsePerRevolution, 1);
		json_uint(serial, "timDiv", cfg->timerDivider, 0);
		json_blockEnd(serial, i != endIndex);
	}
	json_blockEnd(serial, 0);
	json_blockEnd(serial, 0);
}
size_t init_sample_buffer(struct sample *s, const size_t count)
{
        if (s->channel_samples)
                free_sample_buffer(s);

        const size_t size = sizeof(ChannelSample[count]);
        s->channel_samples = (ChannelSample *) portMalloc(size);

        if (NULL == s->channel_samples)
                return 0;

        s->ticks = 0;
        s->channel_count = count;
        init_channel_sample_buffer(getWorkingLoggerConfig(), s);

        return size;
}
static void sendPwmConfig(Serial *serial, size_t startIndex, size_t endIndex){

	json_messageStart(serial);
	json_blockStart(serial, "getPwmCfg");
	for (size_t i = startIndex; i <= endIndex; i++){
		PWMConfig *cfg = &(getWorkingLoggerConfig()->PWMConfigs[i]);
		json_blockStartInt(serial, i);
		json_channelConfig(serial, &(cfg->cfg), 1);
		json_uint(serial, "logPrec", cfg->loggingPrecision, 1);
		json_uint(serial, "outMode", cfg->outputMode, 1);
		json_uint(serial, "logMode", cfg->loggingMode, 1);
		json_uint(serial, "stDutyCyc", cfg->startupDutyCycle, 1);
		json_uint(serial, "stPeriod", cfg->startupPeriod, 1);
		json_float(serial, "vScal", cfg->voltageScaling, DEFAULT_PWM_LOGGING_PRECISION, 0);
		json_blockEnd(serial, i != endIndex); //index
	}
	json_blockEnd(serial, 0);
	json_blockEnd(serial, 0);
}
float timer_get_sample(const int cid)
{
        if (cid >= TIMER_CHANNELS)
                return -1;

        TimerConfig *c = getWorkingLoggerConfig()->TimerConfigs + cid;
        unsigned char ppr = c->pulsePerRevolution;
        switch (c->mode) {
        case MODE_LOGGING_TIMER_RPM:
                return timer_get_rpm(cid) / ppr;
        case MODE_LOGGING_TIMER_FREQUENCY:
                return timer_get_hz(cid) / ppr;
        case MODE_LOGGING_TIMER_PERIOD_MS:
                return timer_get_ms(cid) * ppr;
        case MODE_LOGGING_TIMER_PERIOD_USEC:
                return timer_get_usec(cid) * ppr;
        default:
                return -1;
        }
}
void startConnectivityTask(int16_t priority)
{
        for (size_t i = 0; i < CONNECTIVITY_CHANNELS; i++) {
                g_sampleQueue[i] = create_logger_message_queue();
                if (NULL == g_sampleQueue[i]) {
                        pr_error("conn: err sample queue\r\n");
                        return;
                }
        }

        switch (CONNECTIVITY_CHANNELS) {
        case 1:
                createCombinedTelemetryTask(priority, g_sampleQueue[0]);
                break;
        case 2: {
                ConnectivityConfig *connConfig =
                        &getWorkingLoggerConfig()->ConnectivityConfigs;
                /*
                 * Logic to control which connection is considered 'primary',
                 * which is used later to determine which task has control
                 * over LED flashing
                 */
                const uint8_t cellEnabled =
                        connConfig->cellularConfig.cellEnabled;

                if (cellEnabled)
                        createTelemetryConnectionTask(priority,
                                                      g_sampleQueue[1], 1);

                if (connConfig->bluetoothConfig.btEnabled)
                        createWirelessConnectionTask(priority,
                                                     g_sampleQueue[0],
                                                     !cellEnabled);
        }
                break;
        default:
                pr_error("conn: err init\r\n");
                break;
        }
}
int sim900_init_connection(DeviceConfig *config){
	LoggerConfig *loggerConfig = getWorkingLoggerConfig();
	setCellBuffer(config->buffer, config->length);
	Serial *serial = config->serial;

	pr_debug("init cell connection\r\n");
	int initResult = DEVICE_INIT_FAIL;
	if (0 == initCellModem(serial)){
		CellularConfig *cellCfg = &(loggerConfig->ConnectivityConfigs.cellularConfig);
		TelemetryConfig *telemetryConfig = &(loggerConfig->ConnectivityConfigs.telemetryConfig);
		if (0 == configureNet(serial, cellCfg->apnHost, cellCfg->apnUser, cellCfg->apnPass)){
			pr_info("cell network configured\r\n");
			if( 0 == connectNet(serial, telemetryConfig->telemetryServerHost, TELEMETRY_SERVER_PORT, 0)){
				pr_info("server connected\r\n");
				if (0 == writeAuthJSON(serial, telemetryConfig->telemetryDeviceId)){
					pr_info("server authenticated\r\n");
					initResult = DEVICE_INIT_SUCCESS;
				}
				else{
					pr_error("err: auth- token: ");
					pr_error(telemetryConfig->telemetryDeviceId);
					pr_error("\r\n");
				}
			}
			else{
				pr_error("err: server connect: ");
				pr_error(telemetryConfig->telemetryServerHost);
				pr_error("\r\n");
			}
		}
		else{
			pr_error("Failed to configure network\r\n");
		}
	}
	else{
		pr_warning("Failed to init cell connection\r\n");
	}
	return initResult;
}
/*combined telemetry - for when there's only one telemetry / wireless port available on system
//e.g. "Y-adapter" scenario */
static void createCombinedTelemetryTask(int16_t priority, xQueueHandle sampleQueue)
{
    ConnectivityConfig *connConfig = &getWorkingLoggerConfig()->ConnectivityConfigs;
    size_t btEnabled = connConfig->bluetoothConfig.btEnabled;
    size_t cellEnabled = connConfig->cellularConfig.cellEnabled;

    if (btEnabled || cellEnabled) {
        ConnParams * params = (ConnParams *)portMalloc(sizeof(ConnParams));

        params->periodicMeta = btEnabled && cellEnabled;

        /*defaults*/
        params->check_connection_status = &null_device_check_connection_status;
        params->init_connection = &null_device_init_connection;
        params->serial = SERIAL_TELEMETRY;
        params->sampleQueue = sampleQueue;
        params->connection_timeout = 0;
        params->always_streaming = false;

        if (btEnabled) {
            params->check_connection_status = &bt_check_connection_status;
            params->init_connection = &bt_init_connection;
            params->disconnect = &bt_disconnect;
            params->always_streaming = true;
        }

        /*cell overrides wireless*/
        if (cellEnabled) {
            params->check_connection_status = &sim900_check_connection_status;
            params->init_connection = &sim900_init_connection;
            params->disconnect = &sim900_disconnect;
            params->always_streaming = false;
        }
        xTaskCreate(connectivityTask, (signed portCHAR *) "connTask", TELEMETRY_STACK_SIZE, params, priority, NULL );
    }
}
void createConnectivityTask(){
	g_connParams.sampleQueue = xQueueCreate(SAMPLE_RECORD_QUEUE_SIZE,sizeof( SampleRecord *));
	if (NULL == g_connParams.sampleQueue){
		//TODO log error
		return;
	}

	switch(getWorkingLoggerConfig()->ConnectivityConfigs.connectivityMode){
		case CONNECTIVITY_MODE_CONSOLE:
			g_connParams.check_connection_status = &null_device_check_connection_status;
			g_connParams.init_connection = &null_device_init_connection;
			break;
		case CONNECTIVITY_MODE_BLUETOOTH:
			g_connParams.check_connection_status = &bt_check_connection_status;
			g_connParams.init_connection = &bt_init_connection;
			break;
		case CONNECTIVITY_MODE_CELL:
			g_connParams.check_connection_status = &sim900_check_connection_status;
			g_connParams.init_connection = &sim900_init_connection;
			break;
	}

	xTaskCreate(connectivityTask, (signed portCHAR *) "connTask", TELEMETRY_STACK_SIZE, &g_connParams, TELEMETRY_TASK_PRIORITY, NULL );
}
int api_sampleData(Serial *serial, const jsmntok_t *json){

	int sendMeta = 0;
	if (json->type == JSMN_OBJECT && json->size == 2){
		const jsmntok_t * meta = json + 1;
		const jsmntok_t * value = json + 2;

		jsmn_trimData(meta);
		jsmn_trimData(value);

		if (NAME_EQU("meta",meta->data)){
			sendMeta = modp_atoi(value->data);
		}
	}
	SampleRecord * sr = (SampleRecord *)portMalloc(sizeof(SampleRecord));
	if (sr == 0) return API_ERROR_SEVERE;

	LoggerConfig * config = getWorkingLoggerConfig();
	initSampleRecord(config, sr);
	populateSampleRecord(sr,0, config);
	api_sendSampleRecord(serial, sr, 0, sendMeta);
	portFree(sr);
	return API_SUCCESS_NO_RETURN;
}
void connectivityTask(void *params)
{

    char * buffer = (char *)portMalloc(BUFFER_SIZE);
    size_t rxCount = 0;

    ConnParams *connParams = (ConnParams*)params;
    LoggerMessage msg;

    Serial *serial = get_serial(connParams->serial);

    xQueueHandle sampleQueue = connParams->sampleQueue;
    uint32_t connection_timeout = connParams->connection_timeout;

    DeviceConfig deviceConfig;
    deviceConfig.serial = serial;
    deviceConfig.buffer = buffer;
    deviceConfig.length = BUFFER_SIZE;

    const LoggerConfig *logger_config = getWorkingLoggerConfig();

    bool logging_enabled = false;

    while (1) {
        bool should_stream = logging_enabled ||
                             logger_config->ConnectivityConfigs.telemetryConfig.backgroundStreaming ||
                             connParams->always_streaming;

        while (should_stream && connParams->init_connection(&deviceConfig) != DEVICE_INIT_SUCCESS) {
            pr_info("conn: not connected. retrying\r\n");
            vTaskDelay(INIT_DELAY);
        }

        serial->flush();
        rxCount = 0;
        size_t badMsgCount = 0;
        size_t tick = 0;
        size_t last_message_time = getUptimeAsInt();
        bool should_reconnect = false;

        while (1) {
            if ( should_reconnect )
                break; /*break out and trigger the re-connection if needed */

            should_stream =
                    logging_enabled ||
                    connParams->always_streaming ||
                    logger_config->ConnectivityConfigs.telemetryConfig.backgroundStreaming;

            const char res = receive_logger_message(sampleQueue, &msg,
                                                    IDLE_TIMEOUT);

            /*///////////////////////////////////////////////////////////
            // Process a pending message from logger task, if exists
            ////////////////////////////////////////////////////////////*/
            if (pdFALSE != res) {
                switch(msg.type) {
                case LoggerMessageType_Start: {
                    api_sendLogStart(serial);
                    put_crlf(serial);
                    tick = 0;
                    logging_enabled = true;
                    /* If we're not already streaming trigger a re-connect */
                    if (!should_stream)
                        should_reconnect = true;
                    break;
                }
                case LoggerMessageType_Stop: {
                    api_sendLogEnd(serial);
                    put_crlf(serial);
                    if (! (logger_config->ConnectivityConfigs.telemetryConfig.backgroundStreaming ||
                           connParams->always_streaming))
                        should_reconnect = true;
                    logging_enabled = false;
                    break;
                }
                case LoggerMessageType_Sample: {
                        if (!should_stream)
                                break;

                        const int send_meta = tick == 0 ||
                                (connParams->periodicMeta &&
                                 (tick % METADATA_SAMPLE_INTERVAL == 0));
                        api_send_sample_record(serial, msg.sample, tick, send_meta);

                        if (connParams->isPrimary)
                                toggle_connectivity_indicator();

                        put_crlf(serial);
                        tick++;
                        break;
                }
                default:
                    break;
                }
            }

            /*//////////////////////////////////////////////////////////
            // Process incoming message, if available
            ////////////////////////////////////////////////////////////
            //read in available characters, process message as necessary*/
            int msgReceived = processRxBuffer(serial, buffer, &rxCount);
            /*check the latest contents of the buffer for something that might indicate an error condition*/
            if (connParams->check_connection_status(&deviceConfig) != DEVICE_STATUS_NO_ERROR) {
                pr_info("conn: disconnected\r\n");
                break;
            }
            /*now process a complete message if available*/
            if (msgReceived) {
                last_message_time = getUptimeAsInt();
                pr_debug(connParams->connectionName);
                pr_debug_str_msg(": rx: ", buffer);
                int msgRes = process_api(serial, buffer, BUFFER_SIZE);

                int msgError = (msgRes == API_ERROR_MALFORMED);
                if (msgError) {
                    pr_debug("(failed)\r\n");
                }
                if (msgError) {
                    badMsgCount++;
                } else {
                    badMsgCount = 0;
                }
                if (badMsgCount >= BAD_MESSAGE_THRESHOLD) {
                    pr_warning_int_msg("re-connecting- empty/bad msgs :", badMsgCount );
                    break;
                }
                rxCount = 0;
            }

            /*disconnect if a timeout is configured and
            // we haven't heard from the other side for a while */
            const size_t timeout = getUptimeAsInt() - last_message_time;
            if (connection_timeout && timeout > connection_timeout ) {
                pr_info_str_msg(connParams->connectionName, ": timeout");
                should_reconnect = true;
            }
        }
        clear_connectivity_indicator();
        connParams->disconnect(&deviceConfig);
    }
}
int Lua_GetPWMClockFrequency(lua_State *L)
{
    lua_pushinteger(L,getWorkingLoggerConfig()->PWMClockFrequency);
    return 1;
}
int Lua_GetBackgroundStreaming(lua_State *L)
{
    lua_pushinteger(L, getWorkingLoggerConfig()->ConnectivityConfigs.telemetryConfig.backgroundStreaming == 1);
    return 1;
}