UNPKG

@ncd-io/node-red-enterprise-sensors

Version:

You can install this library through the Palette Manager in Node-Red's UI.

13,057 lines 406 kB
const events = require('events');
const Queue = require('promise-queue');
const fs = require('fs');
const path = require('path');

globalDevices = {};
module.exports = class WirelessSensor{
	constructor(digi){
		this.mac;
		this.digi = digi;
		this.send = digi.send;
		this._emitter = new events.EventEmitter();
		this.sensor_pool = {};
		this.sensor_types = sensor_types(this);
		this.queue = new Queue(1);

		this.sensor_libs = {};
		this.load_sensor_libs();
		//route info and link quality objects
		this.mesh_map = {};
		this.link_quality = {};
		this.query_pool = {};

		this.payloadType = {
			'79': 'manual_sync_check_in',
			'95': 'sync_end',
			'109': 'sync_ack',
			'111': 'sync_check_in',
			'119': 'assert_rsn',
			'122': 'power_up',
			'124': 'config_ack',
			'125': 'config_error',
			'127': 'sensor_data'
		};

		var that = this;
		function receiver(frame){
			try{
				that.parse(frame);
			}catch(e){
				console.log(frame);
				console.log('unable to parse frame');
				console.log(e);
			}
		}
		function routeReceiver(frame){
			that.parse_route_information_packet(frame);
		}
		function linkQualtiyReceiver(frame){
			that.parse_link_quality_packet(frame);
		}

		this.digi.on('receive_packet', receiver);
		this.digi.on('explicit_rx_indicator', linkQualtiyReceiver);
		this.digi.on('route_information_packet', routeReceiver);
		this.on('close', () => {
			//console.log('removing listener');
			this.digi._emitter.removeListener('receive_packet', receiver);
			this.digi._emitter.removeListener('explicit_rx_indicator',linkQualtiyReceiver);
			this.digi._emitter.removeListener('route_information_packet',routeReceiver);
		});
	};
	load_sensor_libs() {
		const sensors_path = path.join(__dirname, 'sensors');
		
		if (fs.existsSync(sensors_path)) {
			fs.readdirSync(sensors_path).forEach(file => {
				if (file.endsWith('.js')) {
					// Require the "factory" function from the file
					const sensorFactory = require(path.join(sensors_path, file));
					
					// We pass 'this.globalDevices' so all libs point to the same object
					const sensor = sensorFactory(globalDevices, this._emitter);
					
					// Register the initialized library by type ID
					this.sensor_libs[sensor.type] = sensor;
				}
			});
		}
	};
	send_control(type, mac, msg){
		if(this.sensor_types[type] && typeof this.sensor_types[type].control != 'undefined'){
			return this.control_send(mac, [249, ...this.sensor_types[type].control(msg)]);
		}else{
			return new Promise((f,r)=>{r('Unknown sensor type');});
		}
	}
	send_arbitrary(mac, data){
		return this.control_send(mac,msg);
	}
	close(cb){
		this._emitter.emit('close');
		this.digi.close();
	}

	parse_route_information_packet(frame){
		var destination = frame.destination_mac;
		var route_array = this.mesh_map[destination];
		var index = this.query_pool[destination];
		//This is the route packet from the modem
		if(frame.source_mac == frame.responder_mac){
			//Add Gateway MAC as first element of array.
			route_array[0] = frame.source_mac;
			//Add Receiver MAC as second item in array
			route_array[1] = frame.receiver_mac;
			if(frame.receiver_mac == frame.destination_mac){
				//if receiver is the sensor then connection is direct from gateway to receiver so emit route info
				var msg_obj = {};
				msg_obj.route = route_array;
				msg_obj.timestamp = Date.now();
				this._emitter.emit("route_info",msg_obj);
			}
		}else{
			//This is the final route packet
			if(frame.receiver_mac == destination){
				route_array.push(frame.receiver_mac);
				var msg_obj = {};
				msg_obj.route = route_array;
				msg_obj.timestamp = Date.now();
				this._emitter.emit("route_info",msg_obj);
				delete this.query_pool[frame.destination_mac];
			}else{
				// This is an intermediate packet
				if(frame.responder_mac == route_array[this.query_pool[destination]]){
					route_array.push(frame.receiver_mac);
				}
			}

		}
		//increment mesh hops index
		this.query_pool[destination]++;
	}

	parse_link_quality_packet(frame){
		console.log("parse_link_quality_packet: "+frame);
		var msg_obj = {};
		msg_obj.source_address = frame.source_mac;
		msg_obj.destination = toMac(frame.data.slice(0,8));
		msg_obj.payload_size = msbLsb(frame.data[8],frame.data[9]);
		msg_obj.iterations = msbLsb(frame.data[10], frame.data[11]);
		msg_obj.successful_iterations = msbLsb(frame.data[12],frame.data[13]);
		msg_obj.retries = msbLsb(frame.data[14],frame.data[15]);
		msg_obj.result = frame.data[16] == 0 ? "success":"failed"
		msg_obj.max_allowed_retries = frame.data[17];
		msg_obj.max_rssi = 0 - frame.data[18];
		msg_obj.min_rssi = 0 - frame.data[19];
		msg_obj.avg_rssi = 0 - frame.data[20];
		msg_obj.timestamp = Date.now();

		this._emitter.emit("link_info",msg_obj);
	}
	get_sensor_definition(sensor_type){
		return this.sensor_libs[sensor_type] || this.sensor_types[sensor_type];
	}
	parse(frame){
		// NOTE this must be above the type selection to prevent rs-485 data from triggering other functionality.
		if(globalDevices.hasOwnProperty(frame.mac) && globalDevices[frame.mac].hasOwnProperty('bridge') && globalDevices[frame.mac].bridge){
			// if(type === undefined && globalDevices.hasOwnProperty(frame.mac) && globalDevices[frame.mac].hasOwnProperty('bridge') && globalDevices[frame.mac].bridge){
			let query_data = {
				addr: frame.mac,
				payload: frame.data,
			}
			if(globalDevices[frame.mac].hasOwnProperty('command_queue') && globalDevices[frame.mac].command_queue.length != 0){
				if(globalDevices[frame.mac].command_queue[0].hasOwnProperty('command')){
					query_data.command = globalDevices[frame.mac].command_queue[0].command;
				}
				if(globalDevices[frame.mac].command_queue[0].hasOwnProperty('meta')){
					query_data.meta = globalDevices[frame.mac].command_queue[0].meta;
				}
				this._emitter.emit('converter_ack-'+frame.mac, query_data);
				return;
			}		
			// }
		}
		var type = this.payloadType[frame.data[0]];
		if(typeof this[type] == 'function'){
			var data = this[type](frame.data.slice(1), frame);
			if(typeof data == 'undefined'){
				return;
			}
			data.type = type;
			data.addr = frame.mac;
			data.received = Date.now();
			data.original = frame;
			var is_new = typeof this.sensor_pool[frame.mac] == 'undefined';
			var new_mode = is_new;
			var mode = (type == 'power_up') ? data.mode : ((type == 'sensor_data') ? 'RUN' : ((type == 'config_ack') ? 'ACK' : 'PGM'));
			if(mode == 'ACK'){
				if(data.data.length == 37){
					this._emitter.emit('manifest_received', data);
				}
			}
			// #OTF
			var otf_devices = [4,12,21,23,26,29,30,32,33,35,39,44,45,48,52,53,54,55,56,58,74,76,78,79,80,81,82,84,88,89,90,91,93,97,98,99,101,102,103,105,106,107,108,109,110,111,112,114,117,118,119,120,121,122,123,124,125,126,180,181,202,217,211,270,519,520,521,531,535,536,537,538,539,540,541,542,543,545,554,1010,1011];
			var device_type = msbLsb(frame.data[6], frame.data[7]);
			// var device_type = frame.data[7];

			if(mode == "RUN"){
				if(frame.data[9] == 70 && frame.data[10] == 76 && frame.data[11] == 89) {
					var broadcast_otf_devices = [101,102,202];
					mode = "FLY";
				}
				if(frame.data[9] == 85 && frame.data[10] == 80 && frame.data[11] == 84 && frame.data[12] == 72 && frame.data[13] == 87 && frame.data[14] == 82 && frame.data[15] == 78) {
					mode = "UPTHWRN";
				}
			}
			if(mode == 'ACK'){
				data.firmware_version = frame.data[5];
				if(data.firmware_version == 0){
					data.sensor_type = frame.data[3];
				}else{
					data.sensor_type = msbLsb(frame.data[3], frame.data[4]);
				}

				// Firmware bug fix to alter received OTN into INI for sync
				// key = sensor type, value = array of affected firmware versions
				// TODO this can be removed if we bypass INI requirement for sync and just trigger on sync_report for both API and device node
				const otn_override_list = {
					110: [10],
					111: [10],
					114: [10]
				};
				if(Object.hasOwn(otn_override_list, data.sensor_type) && otn_override_list[data.sensor_type].includes(data.firmware_version) ){
					if(frame.data[7] == 79 && frame.data[8] == 84 && frame.data[9] == 78){
						console.log('############OVERRIDE OTN###############');
						frame.data[7] = 73;
						frame.data[8] = 78;
						frame.data[9] = 73;
					}
				}

				if(data.sensor_type == 0){
					data.sensor_type = msbLsb(frame.data[3], frame.data[4]);
				}
				if(frame.data[7] == 79 && frame.data[8] == 84 && frame.data[9] == 78){
					mode = "OTN";
				}
				else if(frame.data[7] == 79 && frame.data[8] == 84 && frame.data[9] == 70){
					mode = "OTF";
				}
				else if(frame.data[7] == 73 && frame.data[8] == 78 && frame.data[9] == 73){
					// mode = "FLY";
					mode = "INI";
				}
				else if(frame.data[7] == 69 && frame.data[8] == 78 && frame.data[9] == 68){
					mode = "END";
				}
			}

			// If it is not a new sensor
			if(!is_new){
				// If mode == RUN and type is not 'power_up' don't send RUN
				// sensor_data emitter sets status of UI anyay
				if(mode == 'RUN' && type != 'power_up'){
					new_mode = false;
				} else if(mode == 'RUN' && type == 'power_up'){
					new_mode = true;
				}else{
					new_mode = this.sensor_pool[frame.mac].mode != mode;
				}
			};

			this.sensor_pool[frame.mac] = {
				mac: frame.mac,
				type: data.sensor_type,
				nodeId: data.nodeId,
				mode: mode,
				// data: data,
				lastHeard: data.received
			};

			if(mode === 'FLY' && frame.data.length > 12){
				// get sensor definition from either sensor_types or sensor_libs, maybe do this above if used often
				const sensor_def = this.get_sensor_definition(data.sensor_type);
				// Pull in parse_fly from either if it exists.
				const parse_fly = sensor_def?.parse_fly;
				if(typeof parse_fly === 'function'){
					try{
						this.sensor_pool[frame.mac].reported_config = parse_fly(frame.data);
					} catch(error) {
						console.log('Error detect in parse_fly initiated by FLY message');
						console.log(error);
						this.sensor_pool[frame.mac].reported_config = "Error parsing reported config. See log for details."
					}
				}else{
					console.log('FLY message received but no parse_fly function found for sensor type '+data.sensor_type);
					this.sensor_pool[frame.mac].reported_config = "Error parsing reported config. See log for details."
				}
			}else if(mode === 'OTF' && frame.data.length > 12){
				// restructure and add dead bytes to match FLY message so we only need one parser.
				// If we ever need to add any of the additional information of this packet we will need to rebuild to match
				
				// get sensor definition from either sensor_types or sensor_libs, maybe do this above if used often
				const sensor_def = this.get_sensor_definition(data.sensor_type);
				// Pull in parse_fly from either if it exists.
				const parse_fly = sensor_def?.parse_fly;
				if(typeof parse_fly === 'function'){
					try{
						frame.data.splice(2,0,frame.data[5],0);
						this.sensor_pool[frame.mac].reported_config = parse_fly(frame.data);
					} catch(error){
						console.log('Error detect in parse_fly initiated by OTF message');
						console.log(error);
						this.sensor_pool[frame.mac].reported_config = "Error parsing reported config. See log for details."
					}
				}else{
					console.log('OTF message received but no parse_fly function found for sensor type '+data.sensor_type);
					this.sensor_pool[frame.mac].reported_config = "Error parsing reported config. See log for details."
				}
			}else if(mode === 'ACK'){
				this.sensor_pool[frame.mac].status = data.result;
				if(data.data.length){
					if(data.sensor_type == 110 || data.sensor_type == 111 || data.sensor_type == 112 || data.sensor_type == 114 || data.sensor_type == 127 || data.sensor_type == 543){
						if(data.data[0] == 77 && data.data[1] == 79 && data.data[2] == 70 && data.data[3] == 70 && data.data[4] == 0){
							this.sensor_pool[frame.mac].mode = 'MOFF';
						}
					}
					this.sensor_pool[frame.mac].data = data.data;
				};
			}
			var that = this;

			if(is_new){
				that._emitter.emit('found_sensor', that.sensor_pool[frame.mac]);
			}
			if(mode == 'INI'){
				const sync_data = {
					type: 'sync_init',
					address: frame.mac,
					payload: {
						type: 'sync_init',
						address: frame.mac,
						sensor_type: that.sensor_pool[frame.mac].type,
						node_id: that.sensor_pool[frame.mac].nodeId,
						data: {...that.sensor_pool[frame.mac]}
					},
					description: 'Sensor is remaining powered to accept configurations and updates.',
				}
				that._emitter.emit('sync', sync_data);
				that._emitter.emit('sync-'+frame.mac, sync_data);
				// Emit config_ack to pass enter_otn
				that._emitter.emit('config_ack-'+frame.mac, sync_data);
				return;
			}
			// mode === 'ACK' check added to allow multiple configs through front end gateway input
			if(new_mode || mode === 'ACK'){
				// If RSSI is not enabled send stored values, if it is, request the RSSI and temporarily append it.
				if(typeof frame.rssi == 'undefined'){
					that._emitter.emit('sensor_mode', that.sensor_pool[frame.mac]);
					that._emitter.emit('sensor_mode-'+frame.mac, that.sensor_pool[frame.mac]);
				}else{
					frame.rssi.then((v) => {
						let sensor_pool_rssi = that.sensor_pool[frame.mac];
						sensor_pool_rssi.rssi = v.data[0];
						that._emitter.emit('sensor_mode', sensor_pool_rssi);
						that._emitter.emit('sensor_mode-'+frame.mac, sensor_pool_rssi);
					}).catch(console.log);
				}
			}
			if(mode != 'FLY' && mode !='UPTHWRN'){
				var send_events = function(){
					that._emitter.emit(type, data);
					that._emitter.emit(type+'-'+data.sensor_type, data);
					that._emitter.emit(type+'-'+frame.mac, data);
					// MARK FLY CONFIG DATA
				};
				if(typeof frame.rssi == 'undefined') send_events();
				else frame.rssi.then((v) => {
					data.rssi = v.data[0];
					send_events();
				}).catch(console.log);
			}
		}else if(type == 'assert_rsn'){
			this._emitter.emit(frame.type+'-'+frame.mac.toUpperCase(), data);
			var slicedBytes = frame.data.slice(10);
			var data = {
				'addr': frame.mac,
				'type': msbLsb(frame.data[5], frame.data[4]), // Reversed
				'nodeId': frame.data[1],
				'line_number': frame.data.slice(6, 10).reduce(msbLsb),
				'function_name': bytesToString(slicedBytes)
			};
			this._emitter.emit('assertion_reason', data);
		}else{
			this._emitter.emit(frame.type+'-'+frame.mac.toUpperCase(), data);
			var data = {};
			data.addr = frame.mac;
			data.data = frame.data;
			this._emitter.emit(frame.type+'-'+'unknown_device', data);
		}
	}
	power_up(payload){
		return {
			nodeId: payload[0],
			sensor_type: msbLsb(payload[2], payload[3]),
			mode: String.fromCharCode(...payload.slice(6, 9))
		};
	}
	config_ack(payload){
		if((msbLsb(payload[2], payload[3])) == 28){
			payload[5] = payload[6];
		}
		return {
			nodeId: payload[0],
			counter: payload[1],
			sensor_type: msbLsb(payload[2], payload[3]),
			result: payload[5],
			data: payload.slice(6)
		};
	};
	config_error(payload){
		var errors = [
			'Unknown',
			'Invalid Command',
			'Sensor Type Mismatch',
			'Node ID Mismatch',
			'Apply change command failed',
			'Invalid API Packet Command Response Received After Apply Change Command',
			'Write command failed',
			'Invalid API Packet Command Response Received After Write Command',
			'Parameter Change Command Failed',
			'Invalid Parameter Change Command Response Received After Write Command',
			'Invalid/Incomplete Packet Received',
			'Unknown',
			'Unknown',
			'Unknown',
			'Unknown',
			'Invalid Parameter for Setup/Saving'
		];
		return {
			nodeId: payload[0],
			sensor_type: msbLsb(payload[2], payload[3]),
			error: payload[6],
			error_message: errors[payload[6]],
			last_sent: this.digi.lastSent
		};
	};
	sync_ack(payload, frame){
		const rep_buffer = Buffer.from(frame.data);
		const sensor_type = rep_buffer.readUInt16BE(5);
		let report = {};
		if(rep_buffer[2] === 0xff ){
			const values = this.sensor_libs[sensor_type].sync_parse(rep_buffer);
			try{
				report = {
					type: 'sync_acknowledgment',
					address: frame.mac,
					sensor_type: sensor_type,
					payload: {
						type: 'sync_acknowledgment',
						address: frame.mac,
						sensor_type: sensor_type,
						sync_success: true,
						response_bytes: rep_buffer,
						...values,
					},
					description: 'Acknowledgment received from sensor in response to sync command. This report includes the current sensor information or errors.',
				};
			}catch(e){
				console.log(e);
				console.log('Error in sync_acknowledgement parsing for sensor type '+sensor_type);
			}
			this._emitter.emit('sync_acknowledgment-'+frame.mac, report);
		}else{
			report = {
				type: 'sync_acknowledgment',
				address: frame.mac,
				sensor_type: sensor_type,
				payload: {
					type: 'sync_acknowledgment',
					address: frame.mac,
					sensor_type: sensor_type,
					sync_success: false,
					response_bytes: rep_buffer,
					// ...this.sensor_libs[sensor_type].sync_parse(rep_buffer),
				},
				description: 'Acknowledgment received from sensor in response to sync command. This report includes the current sensor information or errors.',
			};
			this._emitter.emit('sync_acknowledgment_error-'+frame.mac, response);
		}
		report.packet_info = frame.receive_options;
		// console.log(report);
		this._emitter.emit('sync', report);
		this._emitter.emit('sync-'+frame.mac, report);
	};
	manual_sync_check_in(payload, frame){
		const rep_buffer = Buffer.from(frame.data);
		const sensor_type = rep_buffer.readUInt16BE(5);

		// const temp_fly_message = {
		// 	mac: frame.mac,
		// 	type: 114,
		// 	mode: 'PGM',
		// }

		// this._emitter.emit('sensor_mode', temp_fly_message);
		// this._emitter.emit('sensor_mode-'+frame.mac, temp_fly_message);
		
		const values = this.sensor_libs[sensor_type].sync_parse(rep_buffer);

		try{
			const report = {
				type: 'manual_sync_check_in',
				address: frame.mac,
				sensor_type: sensor_type,
				payload: {
					type: 'manual_sync_check_in',
					address: frame.mac,
					sensor_type: sensor_type,
					...values,
				},
				description: 'Manual Sync Check-in received from sensor. This check-in reports sensor information and allows for sync initialization if configuration changes are necessary.',
				'packet_info': frame.receive_options
			};

			this._emitter.emit('sync', report);
			this._emitter.emit('sync-'+frame.mac, report);
		}catch(e){
			console.log(e);
			console.log('Error in manual_sync parsing for sensor type '+sensor_type);
		}
	};
	sync_check_in(payload, frame){
		const rep_buffer = Buffer.from(frame.data);
		const sensor_type = rep_buffer.readUInt16BE(5);

		// const temp_fly_message = {
		// 	mac: frame.mac,
		// 	type: 114,
		// 	mode: 'FLY',
		// }

		// this._emitter.emit('sensor_mode', temp_fly_message);
		// this._emitter.emit('sensor_mode-'+frame.mac, temp_fly_message);

		const values = this.sensor_libs[sensor_type].sync_parse(rep_buffer);

		try{
			const report = {
				type: 'sync_check_in',
				address: frame.mac,
				sensor_type: sensor_type,
				payload: {
					type: 'sync_check_in',
					address: frame.mac,
					sensor_type: sensor_type,
					...values,
				},
				description: 'Check-in received from sensor. This check-in reports sensor information and allows for sync initialization if configuration changes are necessary.',
				'packet_info': frame.receive_options
			};

			this._emitter.emit('sync', report);
			this._emitter.emit('sync-'+frame.mac, report);
		}catch(e){
			console.log(e);
			console.log('Error in sync_check_in parsing for sensor type '+sensor_type);
		}
	};
	sync_end(payload, frame){
		const rep_buffer = Buffer.from(frame.data);
		const sensor_type = rep_buffer.readUInt16BE(5);

		const values = this.sensor_libs[sensor_type].sync_parse(rep_buffer);

		try{
			const report = {
				type: 'sync_end',
				address: frame.mac,
				sensor_type: sensor_type,
				payload: {
					type: 'sync_end',
					address: frame.mac,
					sensor_type: sensor_type,
					...values,
				},
				description: 'Sync process finished. Sensor is reporting its new values after Syn and resuming normal operations.',
				'packet_info': frame.receive_options
			};

			this._emitter.emit('sync', report);
			this._emitter.emit('sync-'+frame.mac, report);
		}catch(e){
			console.log(e);
			console.log('Error in sync_end parsing for sensor type '+sensor_type);
		}
	};
	sensor_data(payload, frame){

		let sensor_type = msbLsb(payload[5], payload[6]);
		let battery_percent = ((msbLsb(payload[2], payload[3]) * 0.361) - 269.66).toFixed(2);
		let voltage = (msbLsb(payload[2], payload[3]) * 0.00322).toFixed(2);

		if(sensor_type == 110 || sensor_type == 111 || sensor_type == 112 || sensor_type == 114 || sensor_type == 127 || sensor_type == 543){
			const vMin = 2.5;
			const vMax = 3.3;
		  
			if (voltage >= vMax){ 
				battery_percent = '100';
			} else if (voltage <= vMin){ 
				battery_percent = '0';
			} else {
				const scale = 5.0; // tuning factor for steepness
				const percent = 100 * (1 - Math.exp(-scale * (voltage - vMin)));
				battery_percent = (Math.min(100, Math.max(0, percent))).toFixed(2);
			}
		}
		var parsed = {
			nodeId: payload[0],
			firmware: payload[1],
			battery: voltage,
			//	battery_percent: (msbLsb(payload[2], payload[3]) * 0.537 - 449.9).toFixed(2),
			battery_percent:  battery_percent,
			counter: payload[4],
			sensor_type: sensor_type
		};

		// #OTF
		var otf_devices = [4,12,21,23,26,29,30,32,33,35,39,44,45,48,52,53,54,55,56,58,74,76,78,79,80,81,82,84,88,89,90,91,93,97,98,99,101,102,103,105,106,107,108,109,110,111,112,114,117,118,119,120,121,122,123,124,125,126,180,181,202,211,217,270,519,520,521,531,535,536,537,538,539,540,541,542,543,545,554,1010,1011];
		if(otf_devices.includes(parsed.sensor_type)){
			// If the message says FLY and there is not FLY timer in progress.
			if(payload[8] == 70 && payload[9] == 76 && payload[10] == 89) {
				parsed.payload = "Fly command";
				return parsed;
			}
		}
		if(frame.data[9] == 85 && frame.data[10] == 80 && frame.data[11] == 84 && frame.data[12] == 72 && frame.data[13] == 87 && frame.data[14] == 82 && frame.data[15] == 78) {
			parsed.payload = "Upthwrn command";
			return parsed;
		}

		// Sensor type 515 has a unique OTF that is indicated by a reserve byte value with MSb of 1
		if(parsed.sensor_type == 515){
			// MSb of reserve byte indicates sub-unit/ct firmware.
			parsed.ct_firmware = payload[7] >> 4;

			// If first bit in reserve is 1 AND current bank equals total banks
			if(payload[7] & 1 && payload[8] == payload[9]){
				this._emitter.emit('set_destination_address'+frame.mac, frame.mac);
				this._emitter.emit('set_destination_address'+parsed.sensor_type, frame.mac);
				parsed.otf_515 = true;
			} else{
				parsed.otf_515 = false;
			}
		}

		if(parsed.sensor_type == 101){
			// If the message says FLY and there is not FLY timer in progress.
			// if(payload[8] == 70 && payload[9] == 76 && payload[10] == 89 && !this.hasOwnProperty('fly_101_in_progress')) {
			// 	this.fly_101_in_progress = true;
			// 	setTimeout(() => {this.config_set_rtc_101('00:00:00:00:00:00:FF:FF')}, 1000);
			// 	return;
			// }

			var deviceAddr = frame.mac;
			var firmware = payload[1];
			var hour = payload[11];
			var minute = payload[12];
			if(firmware == 0){
				var expected_packets = payload[15];
				var current_packet = payload[16];
				var sdata_start = 17;
			}
			else{
				// Added external temp in firmware 1 inserted at item 15
				var expected_packets = payload[17];
				var current_packet = payload[18];
				var sdata_start = 19;
			}


			if(globalDevices.hasOwnProperty(deviceAddr)){
				// if a packet is already stored with the same packet ID, or if packet ID is 1, or if current packet ID is not one more than last packet ID
				if(current_packet == 1 && expected_packets != 1) {
					if(current_packet in globalDevices[deviceAddr].data || !(((current_packet&127)-1) in globalDevices[deviceAddr].data)) {
						console.log('bad packet breakdown, deleting stream. Current packet:');
						console.log(current_packet);
						console.log('Total Expected Packets:');
						console.log(expected_packets);
						if(this.hasOwnProperty('failure_no')){
							this.failure_no = this.failure_no + 1;
						}
						else{
							this.failure_no = 1;
						}
						if(this.hasOwnProperty('failure_no')){
							console.log('####falure no');
							console.log(this.failure_no);
						}
						delete globalDevices[deviceAddr];
						if(current_packet != 1){
							return;
						} else{
							this.build_101_data(payload, deviceAddr, hour, minute, sdata_start, current_packet, firmware);
							return;
						}
					}
				}
				if(expected_packets == 1){
					this.build_101_data(payload, deviceAddr, hour, minute, sdata_start, current_packet, firmware);
				} else{
					globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
				}

				if(Object.keys(globalDevices[deviceAddr].data).length == expected_packets){
					var raw_data = new Array();
					for(const packet in globalDevices[deviceAddr].data){
						raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
					}
					var label = 0;
					// var fft = {
					// 	data: new Array()
					// 	// test: new Array()
					// };
					var fft = new Array();
					var fft_concat = {};

					var en_axis_data = {};
					switch (globalDevices[deviceAddr].en_axis){
						case 1:
							en_axis_data.x_offset = 0;
							en_axis_data.increment = 2;
							break;
						case 2:
							en_axis_data.y_offset = 0;
							en_axis_data.increment = 2;
							break;
						case 3:
							en_axis_data.x_offset = 0;
							en_axis_data.y_offset = 2;
							en_axis_data.increment = 4;
							break;
						case 4:
							en_axis_data.z_offset = 0;
							en_axis_data.increment = 2;
							break;
						case 5:
							en_axis_data.x_offset = 0;
							en_axis_data.z_offset = 2;
							en_axis_data.increment = 4;
							break;
						case 6:
							en_axis_data.y_offset = 0;
							en_axis_data.z_offset = 2;
							en_axis_data.increment = 4;
							break;
						case 7:
							en_axis_data.x_offset = 0;
							en_axis_data.y_offset = 2;
							en_axis_data.z_offset = 4;
							en_axis_data.increment = 6;
							break;
						default:
							en_axis_data.increment = 0;
					}
					var fsr_mult = 0.000305185;
					var fsr_text = "";
					switch(globalDevices[deviceAddr].fsr){
						case 0:
							fsr_mult = 0.000305185;
							break;
						case 1:
							fsr_mult = 0.00061037;
							break;
						case 2:
							fsr_mult = 0.0012207;
							break;
					}
					switch(globalDevices[deviceAddr].fsr){
						case 0:
							fsr_text = "10g";
							break;
						case 1:
							fsr_text = "20g";
							break;
						case 2:
							fsr_text = "40g";
							break;
					}
					for(var i = 0; i < raw_data.length; i+=en_axis_data.increment){
						label++;
						fft_concat[label] = {};

						if('x_offset' in en_axis_data){
							fft_concat[label].x = parseFloat((signInt(((raw_data[i+en_axis_data.x_offset]<<8)+(raw_data[i+en_axis_data.x_offset+1])), 16)*fsr_mult).toFixed(5));
						}
						if('y_offset' in en_axis_data){
							fft_concat[label].y = parseFloat((signInt(((raw_data[i+en_axis_data.y_offset]<<8)+(raw_data[i+en_axis_data.y_offset+1])), 16)*fsr_mult).toFixed(5));
						}
						if('z_offset' in en_axis_data){
							fft_concat[label].z = parseFloat((signInt(((raw_data[i+en_axis_data.z_offset]<<8)+(raw_data[i+en_axis_data.z_offset+1])), 16)*fsr_mult).toFixed(5));
						}
					}
					var fft_concat_obj = {
						time_id: globalDevices[deviceAddr].hour +':'+ globalDevices[deviceAddr].minute,
						mac_address: deviceAddr,
						en_axis: globalDevices[deviceAddr].en_axis,
						odr: globalDevices[deviceAddr].odr,
						device_temp: globalDevices[deviceAddr].device_temp,
						fsr: globalDevices[deviceAddr].fsr,
						data: fft_concat
					};
					if(firmware > 0){
						fft_concat_obj.probe_temp = globalDevices[deviceAddr].probe_temp;
					}
					parsed.sensor_data = fft_concat_obj;
					parsed.raw_packets = globalDevices[deviceAddr].data;
					parsed.raw_data = raw_data;
					delete globalDevices[deviceAddr];
					if(this.hasOwnProperty('failure_no')){
						console.log('####falure no');
						console.log(this.failure_no);
					}

					return parsed;
				}
				else{
					return;
				}
			}else{
				this.build_101_data(payload, deviceAddr, hour, minute, sdata_start, current_packet, firmware);
				return;
			}
		}

		if(parsed.sensor_type == 102){
			// If the message says FLY and there is not FLY timer in progress.
			// if(payload[8] == 70 && payload[9] == 76 && payload[10] == 89 && !this.hasOwnProperty('fly_101_in_progress')) {
			// 	this.fly_101_in_progress = true;
			// 	this.sensor_pool[frame.mac].mode = "FLY";
			// 	this._emitter.emit('sensor_mode-'+frame.mac, this.sensor_pool[frame.mac]);
			// 	// setTimeout(() => {this.config_set_rtc_101('00:00:00:00:00:00:FF:FF')}, 1000);
			// 	setTimeout(() => {this.config_set_rtc_101(frame.mac)}, 1000);
			//
			// 	return;
			// }

			var deviceAddr = frame.mac;
			var firmware = payload[1];
			var hour = payload[9];
			var minute = payload[10];
			var expected_packets = payload[15];
			var current_packet = payload[16];
			var sdata_start = 17;

			if(globalDevices.hasOwnProperty(deviceAddr) || expected_packets == 1){
				// if(expected_packets == 1){
				// 	this.build_102_data(payload, deviceAddr, hour, minute, sdata_start, current_packet, firmware);
				// }

				// if a packet is already stored with the same packet ID,
				 // or if packet ID is 1,
				 // or if current packet ID is not one more than last packet ID
				if(current_packet == 1 && expected_packets != 1) {
					if(current_packet in globalDevices[deviceAddr].data || !(((current_packet&127)-1) in globalDevices[deviceAddr].data)) {
						console.log('bad packet breakdown, deleting stream. Current packet:');
						console.log(current_packet);
						console.log('Total Expected Packets:');
						console.log(expected_packets);
						if(this.hasOwnProperty('failure_no')){
							this.failure_no = this.failure_no + 1;
						}
						else{
							this.failure_no = 1;
						}
						if(this.hasOwnProperty('failure_no')){
							console.log('####falure no');
							console.log(this.failure_no);
						}
						delete globalDevices[deviceAddr];
						if(current_packet != 1){
							return;
						} else{
							this.build_102_data(payload, deviceAddr, hour, minute, sdata_start, current_packet, firmware);
							return;
						}
					}
				}
				if(expected_packets == 1){
					this.build_102_data(payload, deviceAddr, hour, minute, sdata_start, current_packet, firmware);
				} else{
					globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
				}
				if(Object.keys(globalDevices[deviceAddr].data).length == expected_packets){
					var raw_data = new Array();
					for(const packet in globalDevices[deviceAddr].data){
						raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
					}
					var label = 0;

					var fft = new Array();
					var fft_concat = {};

					for(var i = 0; i < raw_data.length; i+=2){
						label++;
						fft_concat[label] = {'v': parseFloat((signInt(((raw_data[i]<<8)+(raw_data[i+1])), 16)*.00322).toFixed(5))};
					}
					var fft_concat_obj = {
						time_id: globalDevices[deviceAddr].hour +':'+ globalDevices[deviceAddr].minute,
						mac_address: deviceAddr,
						// en_axis: globalDevices[deviceAddr].en_axis,
						odr: globalDevices[deviceAddr].odr,
						device_temp: globalDevices[deviceAddr].device_temp,
						probe_temp: globalDevices[deviceAddr].probe_temp,
						data: fft_concat
					};
					parsed.sensor_data = fft_concat_obj;
					// parsed.sensor_data = fft;
					parsed.raw_packets = globalDevices[deviceAddr].data;
					parsed.raw_data = raw_data;
					// var data = globalDevices[deviceAddr];
					delete globalDevices[deviceAddr];
					if(this.hasOwnProperty('failure_no')){
						console.log('####falure no');
						console.log(this.failure_no);
					}

					return parsed;
				}
				else{
					return;
				}

			}else{
				this.build_102_data(payload, deviceAddr, hour, minute, sdata_start, current_packet, firmware);
				return;
			}
		}


		if(payload.length == 179){
			if(msbLsb(payload[2], payload[3]) == 40){
				delete parsed.firmware;
				delete parsed.battery;
				delete parsed.battery_percent;
				delete parsed.counter;
				// parsed.frame_id = payload[1];
				parsed.sensor_type = msbLsb(payload[2], payload[3]);
				var odr;
				switch(payload[4]){
					case 5:
						odr = 400;
						break;
					case 6:
						odr = 800;
						break;
					case 7:
						odr = 1600;
						break;
					case 12:
						odr = 3200;
						break;
					case 13:
						odr = 6400;
						break;
					case 14:
						odr = 12800;
						break;
					case 15:
						odr = 25600;
						break;
					default:
						odr = 0;
				}
				// parsed.sensor_data = {data_type: 'FFT', data: payload.slice(5)};
				var deviceAddr = frame.mac;
				if(deviceAddr in globalDevices){
					globalDevices[deviceAddr] = globalDevices[deviceAddr].concat(payload.slice(5));
					if(globalDevices[deviceAddr].length == 2088){
						var label = 1;
						var fft = {};
						fft['odr'] = odr;
						fft['data_type'] = 'FFT';
						for(var i = 0; i < 2064; i+=6){
							var xLabel = 'x'+label;
							var yLabel = 'y'+label;
							var zLabel = 'z'+label;
							label++;
							fft[xLabel] = ((globalDevices[deviceAddr][i]<<8)+(globalDevices[deviceAddr][i+1]&255))/2048;
							fft[yLabel] = ((globalDevices[deviceAddr][i+2]<<8)+(globalDevices[deviceAddr][i+3]&255))/2048;
							fft[zLabel] = ((globalDevices[deviceAddr][i+4]<<8)+(globalDevices[deviceAddr][i+5]&255))/2048;
						}
						parsed.sensor_data = fft;
						parsed.sensor_data.xbee_data = globalDevices[deviceAddr];
						delete globalDevices[deviceAddr];
						return parsed;
					}else{
						return;
					}
				}else{
					globalDevices[deviceAddr] = payload.slice(5);
					return;
				}
			}
		}else{
			if(typeof this.sensor_types[parsed.sensor_type] == 'undefined' && typeof this.sensor_libs[parsed.sensor_type] == 'undefined'){
				parsed.sensor_data = {
					type: 'unknown',
					data: payload.slice(8)
				};
				// #OTF
			}else if(parsed.sensor_type == 20 || parsed.sensor_type == 21 || parsed.sensor_type == 24 || parsed.sensor_type == 33 || parsed.sensor_type == 39 || parsed.sensor_type == 29 || parsed.sensor_type == 51 || parsed.sensor_type == 54 || parsed.sensor_type == 55 || parsed.sensor_type == 60 || parsed.sensor_type == 80 ||  parsed.sensor_type == 81 || parsed.sensor_type == 82 || parsed.sensor_type == 84 || parsed.sensor_type == 86 || parsed.sensor_type == 87 || parsed.sensor_type == 97 || parsed.sensor_type == 98 || parsed.sensor_type == 99 || parsed.sensor_type == 103 || parsed.sensor_type == 105 || parsed.sensor_type == 110 || parsed.sensor_type == 111 || parsed.sensor_type == 112 || parsed.sensor_type == 114 || parsed.sensor_type == 117 || parsed.sensor_type == 118 || parsed.sensor_type == 127 || parsed.sensor_type == 128 || parsed.sensor_type == 129 || parsed.sensor_type == 130 || parsed.sensor_type == 180 || parsed.sensor_type == 181 || parsed.sensor_type == 202 || parsed.sensor_type == 217 || parsed.sensor_type == 515 || parsed.sensor_type == 518 || parsed.sensor_type == 519 || parsed.sensor_type == 531 || parsed.sensor_type == 537 || parsed.sensor_type == 538 || parsed.sensor_type == 543 || parsed.sensor_type == 545 || parsed.sensor_type == 547 || parsed.sensor_type == 554){
				// new export based parsers
				if(this.sensor_libs.hasOwnProperty(parsed.sensor_type)){
					parsed.sensor_data = this.sensor_libs[parsed.sensor_type].parse(payload, parsed, frame.mac);
					parsed.sensor_name = this.sensor_libs[parsed.sensor_type].name;
				}else{
					parsed.sensor_data = this.sensor_types[parsed.sensor_type].parse(payload, parsed, frame.mac);
					parsed.sensor_name = this.sensor_types[parsed.sensor_type].name;
				}
				if(!parsed.sensor_data){
					return;
				}
			}
			else{
				// new export based parsers
				if(this.sensor_libs.hasOwnProperty(parsed.sensor_type)){
					parsed.sensor_data = this.sensor_libs[parsed.sensor_type].parse(payload.slice(8), payload);
					parsed.sensor_name = this.sensor_libs[parsed.sensor_type].name;
				}else{
					parsed.sensor_data = this.sensor_types[parsed.sensor_type].parse(payload.slice(8), payload);
					parsed.sensor_name = this.sensor_types[parsed.sensor_type].name;	
				}
			}
		}
		return parsed;
	}
	build_sync_command(configs, is_wireless_node = false) {
		const config_map = this.sensor_libs[configs.sensor_type].get_config_map(configs.firmware_version);
		
		// Get the buffer size defined in library for this specific firmware
		const size = this.sensor_libs[configs.sensor_type].get_write_buffer_size(configs.firmware_version); 
		const buf = Buffer.alloc(size, 0);
		// insert command header
		buf.set([0x6c, 0x00, 0x00], 0);
		let desired_configs = {};
		if(is_wireless_node){
			desired_configs = { ...configs.reported_configs };
		}else{
			desired_configs = { ...configs.reported_configs, ...configs.desired_configs };
		}

		for (const [key, value] of Object.entries(desired_configs)) {
			const map_item = config_map[key];
			if (!map_item || map_item.write_index === undefined) continue;
	
			const { write_index: idx, validator: { type } } = map_item;
	
			switch (type) {
				case 'uint8':    buf.writeUInt8(Number(value), idx); break;
				case 'uint16be': buf.writeUInt16BE(Number(value), idx); break;
				case 'uint16le': buf.writeUInt16LE(Number(value), idx); break;
				case 'uint32be': buf.writeUInt32BE(Number(value), idx); break;
				case 'hex':
					Buffer.from(value, 'hex').copy(buf, idx);
				case 'mac':
					Buffer.from(value, 'hex').copy(buf, idx);
					break;
			}
		}		
		return buf;
	};
	send_sync_configs(addr, configs){
		console.log('send_sync_configs command');

		// [...] converts the buffer to an array of integers, which is the expected format for the radio
		const packet = [...this.build_sync_command(configs)];
		console.log('Built sync packet: ');
		console.log(packet);
		return this.config_send_sync(addr, packet);
	};
	get_intended_wireless_node_configs(data, configs){
		const config_map = this.sensor_libs[data.payload.machine_values.sensor_type].get_config_map(data.payload.machine_values.firmware_version);
		const html_map = {};
		for(const [key, value] of Object.entries(config_map)){
			if(value.html_id){
				html_map[value.html_id] = key;
			}
			// else{
			// 	html_map[key] = key;
			// }
		}
		const response = {};
		for (let [key, value] of Object.entries(configs)) {
			// skip anything with includes_active since those are just indicators for whether or not to include the config item
			if(!key.includes('_active')){
				const is_node_id_or_delay = key === 'node_id' || key === 'delay';
				let active_key = '';
				if(is_node_id_or_delay){
					active_key = 'node_id_delay_active';
				}else if(Object.hasOwn(html_map, key) && Object.hasOwn(config_map[html_map[key]], 'html_active_id')){
					active_key = config_map[html_map[key]].html_active_id;
				}else{
					active_key = key+'_active';
				}
				if(Object.hasOwn(html_map, key) && configs[active_key] === true){
					const map_item = config_map[html_map[key]];
					if (!map_item || map_item.write_index === undefined) continue;
					switch (config_map[html_map[key]].validator.type) {
						case 'hex':
							// ensure hex values are even length and do not include 0x
							value = value.replace(/:/g, '').toLowerCase();
							break;
						case 'mac':
							value = value.replace(/:/g, '').toLowerCase();
							break;
					}
					response[html_map[key]] = map_item;
					response[html_map[key]].html_value = value;
				}
			}
		}
		return response;
	};
	build_sync_command_wireless_node(data, html_map, api_configs) {
		console.log('build_sync_command_wireless_node');

		// Get the buffer size defined in library for this specific firmware
		// const size = this.sensor_libs[data.payload.sensor_type].get_write_buffer_size(data.payload.firmware_version); 
		// const buf = Buffer.alloc(size, 0);
		// insert command header
		// buf.set([0x6c, 0x00, 0x00], 0);
		// const desired_configs = { ...configs.reported_configs, ...configs.desired_configs };

		const buf = this.build_sync_command(api_configs, true);

		for (const [key, value] of Object.entries(html_map)) {
			if (value.write_index === undefined) continue;
	
			const { write_index: idx, validator: { type } } = value;

			switch (type) {
				case 'uint8':    buf.writeUInt8(Number(value.html_value), idx); break;
				case 'uint16be': buf.writeUInt16BE(Number(value.html_value), idx); break;
				case 'uint32be': buf.writeUInt32BE(Number(value.html_value), idx); break;
				case 'hex':
					Buffer.from(value.html_value, 'hex').copy(buf, idx);
					break;
				case 'mac':
					Buffer.from(value.html_value, 'hex').copy(buf, idx);
					break;
			}
			
		}
		return buf;
	}
	send_sync_config_wireless_node(data, configs, api_configs){
		console.log('send_sync_config_wireless_node command');
		// [...] converts the buffer to an array of integers, which is the expected format for the radio
		const packet = [...this.build_sync_command_wireless_node(data, configs, api_configs)];
		console.log('Built sync packet: ');
		console.log(packet);
		return this.config_send_sync(data.address, packet);
	};
	firmware_set_to_ota_mode(sensor_mac){
		console.log('firmware_set_to_ota_mode');
		var packet = [245, 56, 0, 0, 0];
		return this.config_send(sensor_mac, packet, {}, 5000);
		// return this.config_send(sensor_mac, packet);
	}
	// TODO no code basis
	firmware_exit_ota_mode(sensor_mac){
		console.log('firmware_exit_ota_mode');
		var packet = [245, 57, 0, 0, 0];
		return this.config_send(sensor_mac, packet);
	}
	firmware_request_manifest(sensor_mac){
		console.log('firmware_request_manifest');
		var packet = [245, 60, 0, 0, 0];
		return this.config_send(sensor_mac, packet, {}, 1500, 500);
	}
	firmware_send_manifest(sensor_mac, manifest){
		console.log('firmware_send_manifest');
		// sensor_mac = "00:00:00:00:00:00:ff:ff";
		let packet = [245, 58, 0, 0, 0].concat(Array.prototype.slice.call(manifest));
		return this.config_send(sensor_mac, packet, {}, 1500, 500);
	}
	firmware_send_manifest_v13(sensor_mac, manifest){
		console.log('firmware_send_manifest_v13');
		// sensor_mac = "00:00:00:00:00:00:ff:ff";
		let packet = [245, 58, 0, 0, 0].concat(Array.prototype.slice.call(manifest));
		return this.firmware_send_v13(sensor_mac, packet, {}, 7000, 140, true);
	}
	firmware_send_chunk(sensor_mac, offset, chunk){
		console.log('firmware_send_chunk');
		// sensor_mac = "00:00:00:00:00:00:ff:ff";
		let packet = [245, 59, 0, 0, 0].concat(offset, Array.prototype.slice.call(chunk));
		// console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	firmware_request_last_segment(sensor_mac){
		console.log('firmware_request_last_segment');
		let packet = [245, 61, 0, 0, 0];
		// console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_reboot_sensor(sensor_mac){
		console.log('config_reboot_sensor: '+sensor_mac)
		var packet = [247, 64, 0, 0, 0];
		return this.config_send(sensor_mac, packet);
	}
	config_set_broadcast(sensor_mac){
		return config_set_destination(sensor_mac, 0x0000FFFF);
	}
	config_set_destination(sensor_mac, modem_mac){
		console.log('config_set_destination');
		console.log(modem_mac);
		var packet = [247, 3, 0, 0, 0];
		var bytes = int2Bytes(modem_mac, 4);
		packet.push(...bytes);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_id_delay(sensor_mac, node_id, delay_s){
		console.log('config_set_id_delay');
		var packet = [247, 2, 0, 0, 0, node_id];
		var delay_b = int2Bytes(delay_s, 3);
		packet.push(...delay_b);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_only_delay(sensor_mac, delay_s){
		console.log('config_set_only_delay');
		var packet = [247, 2, 0, 0, 0, 0];
		var delay_b = int2Bytes(delay_s, 3);
		packet.push(...delay_b);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_power(sensor_mac, pwr){
		var packet = [247, 4, 0, 0, 0, pwr];
		return this.config_send(sensor_mac, packet);
	}
	config_set_pan_id(sensor_mac, pan_id){
		var packet = [247, 5, 0, 0, 0];
		packet.push(...int2Bytes(pan_id, 2));
		return this.config_send(sensor_mac, packet);
	}
	config_set_retries(sensor_mac, retries){
		var packet = [247, 6, 0, 0, 0, retries];
		return this.config_send(sensor_mac, packet);
	}
	config_set_change_detection(sensor_mac, enabled, perc, interval){
		console.log('config_set_change_detection_ch1');
		if(!perc) perc = 0;
		if(!interval) interval = 0;
		var packet = [247, 7, 0, 0, 0, enabled, perc, interval >> 16, (interval >> 8) & 255, interval & 255];
		return this.config_send(sensor_mac, packet);
	}
	config_set_change_detection_ch2(sensor_mac, enabled, perc, interval){
		console.log('config_set_change_detection_ch2');
		if(!perc) perc = 0;
		if(!interval) interval = 0;
		var packet = [247, 8, 0, 0, 0, enabled, perc, interval >> 16, (interval >> 8) & 255, interval & 255];
		return this.config_send(sensor_mac, packet);
	}
	config_set_change_detection_ch3(sensor_mac, enabled, perc, interval){
		console.log('config_set_change_detection_ch3');
		if(!perc) perc = 0;
		if(!interval) interval = 0;
		var packet = [247, 9, 0, 0, 0, enabled, perc, interval >> 16, (interval >> 8) & 255, interval & 255];
		return this.config_send(sensor_mac, packet);
	}
	config_set_bp_altitude(sensor_mac, alt){
		var packet = [244, 1, 0, 0, 0, alt >> 8, alt & 255];
		return this.config_send(sensor_mac, packet);
	}
	config_set_bp_pressure(sensor_mac, press){
		var packet = [244, 4, 0, 0, 0, press >> 8, press & 255];
		return this.config_send(sensor_mac, packet);
	}
	config_set_bp_temp_precision(sensor_mac, prec){
		var packet = [244, 2, 0, 0, 0, prec];
		return this.config_send(sensor_mac, packet);
	}
	config_set_bp_press_precision(sensor_mac, prec){
		var packet = [244, 3, 0, 0, 0, prec];
		return this.config_send(sensor_mac, packet);
	}
	config_set_amgt_accel(sensor_mac, range){
		var packet = [244, 1, 0, 0, 0, range];
		return this.config_send(sensor_mac, packet);
	}
	config_set_amgt_magnet(sensor_mac, gain){
		var packet = [244, 2, 0, 0, 0, gain];
		return this.config_send(sensor_mac, packet);
	}
	config_set_amgt_gyro(sensor_mac, scale){
		var packet = [244, 3, 0, 0, 0, scale];
		return this.config_send(sensor_mac, packet);
	}
	config_set_filtering(sensor_mac, enable){
		var packet = [244, 2, 0, 0, 0, enable];
		return this.config_send(sensor_mac, packet);
	}
	config_set_data_rate(sensor_mac, data_rate){
		var packet = [244, 3, 0, 0, 0, data_rate];
		return this.config_send(sensor_mac, packet);
	}
	config_set_time_series(sensor_mac, time_series){
		var packet = [244, 8, 0, 0, 0, time_series];
		return this.config_send(sensor_mac, packet);
	}
	config_set_reading_type(sensor_mac, reading_type){
		var packet = [244, 4, 0, 0, 0, reading_type];
		return this.config_send(sensor_mac, packet);
	}
	config_set_motion_threshold_46(sensor_mac, value){
		console.log('config_set_motion_threshold_46');
		let packet = [244, 1, 0, 0, 0];
		let threshold = int2Bytes((value), 4);
		packet.push(...threshold);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}

	config_set_acceleration_range_24(sensor_mac, value){
		console.log('config_set_acceleration_range_24');
		var packet = [244, 1, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_periodic_check_rate_76(sensor_mac, value){
		console.log('config_set_periodic_check_rate');
		let rate = int2Bytes((value), 2);
		var packet = [244, 32, 0, 0, 0];
		packet.push(...rate);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_ppm_threshold_76(sensor_mac, value){
		console.log('config_set_ppm_threshold_76');
		let rate = int2Bytes((value), 2);
		var packet = [244, 36, 0, 0, 0];
		// packet.push(...rate);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_alert_duration_76(sensor_mac, value){
		console.log('config_set_alert_duration_76');
		var packet = [244, 38, 0, 0, value];
		// packet.push(...rate);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sensor_boot_time_76(sensor_mac, value){
		console.log('config_set_sensor_boot_time_76');
		var packet = [244, 36, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_data_rate_24(sensor_mac, value){
		console.log('config_set_data_rate_24');
		var packet = [244, 2, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_threshold_24(sensor_mac, value){
		console.log('config_set_threshold_24');
		var packet = [244, 3, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_duration_24(sensor_mac, value){
		console.log('config_set_duration_24');
		var packet = [244, 4, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_interrupt_24(sensor_mac, value){
		console.log('config_set_interrupt_24');
		var packet = [244, 9, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	// config_set_impact_accel(sensor_mac, range){
	// 	var packet = [244, 1, 0, 0, 0, range];
	// 	return this.config_send(sensor_mac, packet);
	// }
	// config_set_impact_data_rate(sensor_mac, rate){
	// 	var packet = [244, 2, 0, 0, 0, rate];
	// 	return this.config_send(sensor_mac, packet);
	// }
	// config_set_impact_threshold(sensor_mac, threshold){
	// 	var packet = [244, 3, 0, 0, 0, threshold];
	// 	return this.config_send(sensor_mac, packet);
	// }
	// config_set_impact_duration(sensor_mac, duration){
	// 	var packet = [244, 4, 0, 0, 0, duration];
	// 	return this.config_send(sensor_mac, packet);
	// }

	config_set_sensor_forced_calibration(sensor_mac, value){
		var packet = [244, 31, 0, 0, 0];
		var cal_val = int2Bytes(value, 2);
		packet.push(...cal_val);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sensor_forced_calibration_535(sensor_mac){
		console.log('config_set_sensor_forced_calibration_535');
		// convert before processing
		var packet = [244, 32, 0, 0, 0];
		return this.config_send(sensor_mac, packet);
	}
	config_set_sensor_temperature_offset_44(sensor_mac, value){
		console.log('config_set_sensor_temperature_offset_44');
		// convert before processing
		value = value * 100;
		var packet = [244, 30, 0, 0, 0];
		var cal_val = int2Bytes(value, 2);
		packet.push(...cal_val);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_output_data_rate_p2_81(sensor_mac, output_rate){
		console.log('config_set_output_data_rate_p2_81');
		var packet = [244, 79, 0, 0, 101, 36, output_rate];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sampling_duration_p2_81(sensor_mac, sampling_duration){
		console.log('config_set_sampling_duration_p2_81');
		var packet = [244, 79, 0, 0, 101, 38, sampling_duration];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_output_data_rate_101(sensor_mac, output_rate){
		console.log('config_set_output_data_rate_101');
		var packet = [244, 79, 0, 0, 101, 0, output_rate];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_get_output_data_rate_101(sensor_mac, output_rate){
		console.log('config_get_output_data_rate_101');
		var packet = [244, 79, 0, 0, 101, 1];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sampling_duration_101(sensor_mac, sampling_duration){
		console.log('config_set_sampling_duration_101');
		var packet = [244, 79, 0, 0, 101, 2, sampling_duration];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_get_sampling_duration_101(sensor_mac, sampling_duration){
		console.log('config_get_sampling_duration_101');
		var packet = [244, 79, 0, 0, 101, 3];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_axis_enabled_101(sensor_mac, x_axis, y_axis, z_axis){
		var axis_value = 0;
		console.log('config_set_axis_enabled_101');
		if(x_axis){
			axis_value+=1;
		}
		if(y_axis){
			axis_value+=2;
		}
		if(z_axis){
			axis_value+=4;
		}
		var packet = [244, 79, 0, 0, 101, 4, axis_value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_get_axis_enabled_101(sensor_mac, x_axis, y_axis, z_axis){
		var axis_value = x_axis+y_axis+z_axis;
		console.log('config_get_axis_enabled_101');
		var packet = [244, 79, 0, 0, 101, 5];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sampling_interval_101(sensor_mac, sampling_interval){
		console.log('config_set_sampling_interval_101');
		var packet = [244, 79, 0, 0, 101, 6, sampling_interval];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sampling_interval_202(sensor_mac, sampling_interval){
		console.log('config_set_sampling_interval_202');
		var packet = [244, 0, 0, 0, 0, sampling_interval];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_probe_boot_time_202(sensor_mac, value){
		console.log('config_set_probe_boot_time_202');
		var packet = [244, 83, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_get_sampling_interval_101(sensor_mac, sampling_interval){
		console.log('config_get_sampling_interval_101');
		var packet = [244, 79, 0, 0, 101, 7];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_full_scale_range_101(sensor_mac, range){
		console.log('config_set_full_scale_range_101');
		var packet = [244, 79, 0, 0, 101, 11, range];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_get_full_scale_range_101(sensor_mac, range){
		console.log('config_get_full_scale_range_101');
		var packet = [244, 79, 0, 0, 101, 12];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}

	config_set_operation_mode_80(sensor_mac, mode){
		console.log('config_set_operation_mode');
		console.log(mode);
		var packet = [244, 79, 0, 0, 0, 9, mode];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_filters_80(sensor_mac, filter){
		console.log('config_set_filters_80');
		var packet = [244, 79, 0, 0, 0, 13, filter];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_low_pass_filter_80(sensor_mac, lp_filter){
		console.log('config_set_low_pass_filters_80');
		var packet = [244, 79, 0, 0, 80, 52, lp_filter];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_high_pass_filter_80(sensor_mac, hp_filter){
		console.log('config_set_high_pass_filters_80');
		var packet = [244, 79, 0, 0, 80, 54, hp_filter];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_low_pass_filter_81_p2(sensor_mac, lp_filter){
		console.log('config_set_low_pass_filter_81_p2');
		var packet = [244, 79, 0, 0, 80, 56, lp_filter];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_high_pass_filter_81_p2(sensor_mac, hp_filter){
		console.log('config_set_high_pass_filter_81_p2');
		var packet = [244, 79, 0, 0, 80, 58, hp_filter];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_measurement_mode_80(sensor_mac, mode){
		console.log('config_set_measurement_mode_80');
		var packet = [244, 79, 0, 0, 0, 15, mode];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_on_request_timeout_80(sensor_mac, timeout){
		console.log('config_set_on_request_timeout_80');
		var packet = [244, 79, 0, 0, 0, 17, timeout];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_deadband_80(sensor_mac, timeout){
		console.log('config_set_deadband_80');
		var packet = [244, 79, 0, 0, 0, 40, timeout];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_current_calibration_individual_80(sensor_mac, value, channel_target){
		console.log('config_set_current_calibration_individual_82');
		var packet = [244, channel_target, 0, 0, 13];
		var cal_val = int2Bytes((value*100), 2);
		packet.push(...cal_val);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_current_calibration_82(sensor_mac, value){
		console.log('current_calibration_82');
		var packet = [244, 79, 0, 0, 0, 34];
		var cal_val = int2Bytes(value, 4);
		packet.push(...cal_val);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_led_alert_mode_84(sensor_mac, value){
		console.log('config_set_led_alert_mode_84');
		var packet = [244, 79, 0, 0, 0, 66, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_led_accelerometer_threshold_84(sensor_mac, value){
		console.log('config_set_led_accelerometer_threshold_84');
		var packet = [244, 79, 0, 0, 0, 62, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_led_velocity_threshold_84(sensor_mac, value){
		console.log('config_set_led_velocity_threshold_84');
		var packet = [244, 79, 0, 0, 0, 64, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_get_sampling_interval_101(sensor_mac, sampling_interval){
		console.log('config_get_sampling_interval_101');
		var packet = [244, 79, 0, 0, 101, 7];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_full_scale_range_101(sensor_mac, range){
		console.log('config_set_full_scale_range_101');
		var packet = [244, 79, 0, 0, 101, 11, range];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_get_full_scale_range_101(sensor_mac, range){
		console.log('config_get_full_scale_range_101');
		var packet = [244, 79, 0, 0, 101, 12];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_rtc_101(sensor_mac){
		console.log('config_set_rtc_101');
		console.log(sensor_mac);
		var date = new Date();
		var packet = [244, 79, 0, 0, 101, 8, date.getHours(), date.getMinutes(), date.getSeconds()];
		console.log(packet);
		delete this.fly_101_in_progress;
		return this.config_send(sensor_mac, packet);
	}
	config_set_rtc_202(sensor_mac){
		console.log('config_set_rtc_202');
		console.log(sensor_mac);
		var date = new Date();
		var packet = [244, 8, 0, 0, 0, date.getHours(), date.getMinutes(), date.getSeconds()];
		console.log(packet);
		delete this.fly_101_in_progress;
		return this.config_send(sensor_mac, packet);
	}
	// config_set_roll_threshold_47(sensor_mac, threshold){
	// 	console.log('config_set_roll_threshold_47');
	// 	var packet = [244, 1, 0, 0, 47, 0, threshold];
	// 	console.log(packet);
	// 	return this.config_send(sensor_mac, packet);
	// }
	// config_set_pitch_threshold_47(sensor_mac, threshold){
	// 	console.log('config_set_pitch_threshold_47');
	// 	var packet = [244, 3, 0, 0, 47, 0, threshold];
	// 	console.log(packet);
	// 	return this.config_send(sensor_mac, packet);
	// }
	config_set_accelerometer_threshold_108(sensor_mac, value){
		console.log('config_set_accelerometer_threshold_108');
		var packet = [244, 32, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_debounce_time_108(sensor_mac, value){
		console.log('config_set_debounce_time_108');
		var packet = [244, 39, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_debounce_time_v10_108(sensor_mac, value){
		console.log('config_set_debounce_time_v10_108');
		var packet = [244, 39, 0, 0, 0];
		let threshold = int2Bytes((value), 2);
		packet.push(...threshold);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_clear_timers_108(sensor_mac, value){
		console.log('config_clear_timers_108');
		var packet = [244, 36, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_accelerometer_state_108(sensor_mac, value){
		console.log('config_set_accelerometer_state_108');
		var packet = [244, 37, 0, 0, 0, 7, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_input_one_108(sensor_mac, value){
		console.log('config_set_input_one_108');
		var packet = [244, 41, 0, 0, 0, 1, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_input_two_108(sensor_mac, value){
		console.log('config_set_input_two_108');
		var packet = [244, 41, 0, 0, 0, 2, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_input_three_108(sensor_mac, value){
		console.log('config_set_input_three_108');
		var packet = [244, 41, 0, 0, 0, 3, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_counter_threshold_108(sensor_mac, value){
		console.log('config_set_counter_threshold_108');
		var packet = [244, 43, 0, 0, 0];
		let threshold = int2Bytes((value), 4);
		packet.push(...threshold);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_push_notification_108(sensor_mac, value){
		console.log('config_set_push_notification_108');
		var packet = [244, 45, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_deactivate_activate_accelero_108(sensor_mac, value){
		console.log('config_set_deactivate_activate_accelero_108');
		var packet = [244, 37, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_reset_timeout_108(sensor_mac, value){
		console.log('config_set_reset_timeout_108');
		var packet = [244, 49, 0, 0, 0];
		let timeout = int2Bytes((value), 2);
		packet.push(...timeout);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_reset_mode_to_disabled_108(sensor_mac, value){
		console.log('config_set_reset_mode_to_disabled_108');
		var packet = [244, 51, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_quality_of_service_108(sensor_mac, value){
		console.log('config_set_quality_of_service_108');
		var packet = [247, 52, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_rtc_108(sensor_mac){
		console.log('config_set_rtc_108');
		var date = new Date();
		var packet = [244, 53, 0, 0, 0, date.getHours(), date.getMinutes(), date.getSeconds()];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_transmission_interval_108(sensor_mac, value){
		console.log('config_set_transmission_interval_108');
		var packet = [244, 55, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_shift_one_108(sensor_mac, hours, minutes){
		console.log('config_set_shift_one_108');
		var packet = [244, 47, 0, 0, 0, 0, hours, minutes];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_shift_two_108(sensor_mac, hours, minutes){
		console.log('config_set_shift_two_108');
		var packet = [244, 47, 0, 0, 0, 1, hours, minutes];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_shift_three_108(sensor_mac, hours, minutes){
		console.log('config_set_shift_three_108');
		var packet = [244, 47, 0, 0, 0, 2, hours, minutes];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_shift_four_108(sensor_mac, hours, minutes){
		console.log('config_set_shift_four_108');
		var packet = [244, 47, 0, 0, 0, 3, hours, minutes];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_fly_interval_108(sensor_mac, value){
		console.log('config_set_fly_interval_108');
		var packet = [247, 56, 0, 0, 0];
		let val = int2Bytes((value), 2);
		packet.push(...val);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sample_rate_108(sensor_mac, value){
		console.log('config_set_sample_rate_108');
		var packet = [244, 57, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sample_rate_108(sensor_mac, value){
 		console.log('config_set_sample_rate_108');
 		var packet = [244, 57, 0, 0, 0, value];
 		console.log(packet);
		return this.config_send(sensor_mac, packet, {}, 6000);
 	}
	config_set_sensor_boot_time_420ma(sensor_mac, value){
		console.log('sensor_boot_time_420ma');
		var packet = [244, 68, 0, 0, 45, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sensor_boot_time_78(sensor_mac, value){
		console.log('config_set_sensor_boot_time_78');
		var packet = [244, 78, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_clear_stored_calibration_65(sensor_mac){
		console.log('config_clear_stored_calibration_65');
		var packet = [244, 88, 0, 0, 65, 1];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_atmospheric_calibration_command_65(sensor_mac){
		console.log('config_send_atmospheric_calibration_command_65');
		var packet = [244, 88, 0, 0, 65, 2];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_calibrate_zero_point_65(sensor_mac){
		console.log('config_calibrate_zero_point_65');
		var packet = [244, 88, 0, 0, 65, 3];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_payload_length_80(sensor_mac, value){
		console.log('config_set_payload_length_80');
		var packet = [244, 79, 0, 0, 80, 68, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_counter_threshold_35(sensor_mac, value){
		console.log('config_set_counter_threshold_35');
		let packet = [244, 1, 0, 0, 23];
		let threshold = int2Bytes((value), 2);
		packet.push(...threshold);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}

	config_set_low_calibration_420ma(sensor_mac, value){
		console.log('config_set_low_calibration_420ma');
		let packet = [244, 66, 0, 0, 45, 1];
		let calibration = int2Bytes((value), 4);
		packet.push(...calibration);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_mid_calibration_420ma(sensor_mac, value){
		console.log('config_set_mid_calibration_420ma');
		let packet = [244, 66, 0, 0, 45, 2];
		let calibration = int2Bytes((value), 4);
		packet.push(...calibration);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_high_calibration_420ma(sensor_mac, value){
		console.log('config_set_high_calibration_420ma');
		let packet = [244, 66, 0, 0, 45, 3];
		let calibration = int2Bytes((value), 4);
		packet.push(...calibration);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_thermocouple_type_23(sensor_mac, value){
		console.log('config_set_thermocouple_type_23');
		let packet = [244, 83, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_filter_thermocouple(sensor_mac, value){
		console.log('config_set_filter_thermocouple');
		let packet = [244, 85, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_cold_junction_thermocouple(sensor_mac, value){
		console.log('config_set_cold_junction_thermocouple');
		let packet = [244, 87, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sample_resolution_thermocouple(sensor_mac, value){
		console.log('config_set_sample_resolution_thermocouple');
		let packet = [244, 89, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_number_of_samples_thermocouple(sensor_mac, value){
		console.log('config_set_number_of_samples_thermocouple');
		let packet = [244, 91, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_measurement_type_thermocouple(sensor_mac, value){
		console.log('config_set_number_of_samples_thermocouple');
		let packet = [244, 95, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_debounce_time_2(sensor_mac, value){
		console.log('config_set_debounce_time_2');
		var packet = [244, 66, 0, 0, 2, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_debounce_time_35(sensor_mac, value){
		console.log('config_set_debounce_time_35');
		var packet = [244, 3, 0, 0, 35, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_auto_check_interval_88(sensor_mac, value){
		console.log('config_set_auto_check_interval_88');
		let packet = [244, 70, 0, 0, 88];
		let interval = int2Bytes((value), 2);
		packet.push(...interval);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_auto_check_threshold_88(sensor_mac, value){
		console.log('config_set_auto_check_threshold_88');
		let packet = [244, 72, 0, 0, 88, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_stay_on_mode_539(sensor_mac, value){
		console.log('config_set_stay_on_mode_539');
		var packet = [247, 50, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_baudrate_539(sensor_mac, value){
		console.log('config_set_baudrate_539');
		var packet = [244, 32, 0, 0, 23, 0];
		let baudrate = int2Bytes((value), 3);
		packet.push(...baudrate);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_rx_timeout_539(sensor_mac, value){
		console.log('config_set_rx_timeout_539');
		var packet = [244, 34, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_bootup_time_539(sensor_mac, value){
		console.log('config_set_bootup_time_539');
		var packet = [244, 36, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sensor_add_539(sensor_mac, value){
		console.log('config_set_sensor_add_539');
		var packet = [244, 38, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sub_device_type_539(sensor_mac, value){
		console.log('config_set_sub_device_type_539');
		var packet = [244, 40, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	// config_set_number_of_regs_to_rd_539(sensor_mac, value){
	// 	console.log('config_set_number_of_regs_to_rd_539');
	// 	var packet = [244, 51, 0, 0, 23, value];
	// 	console.log(packet);
	// 	return this.config_send(sensor_mac, packet);
	// }
	// config_set_reg_539(sensor_mac, value, register){
	// 	console.log('config_set_reg_539');
	// 	// var packet = [244, 51, 0, 0, 23, ]
	// 	var packet = [244, 30 + register, 0, 0, 23];
	// 	let value_reg = int2Bytes((value), 2);
	// 	packet.push(...value_reg);
	// 	console.log(packet);
	// 	return this.config_send(sensor_mac, packet);
	// }
	config_set_all_register_data_539(sensor_mac, number_of_registers, registers){
		console.log('config_set_all_registers_539');
		// registers is an arbitrary number of 16 bit integers
		// var packet = [244, 53, 0, 0, 23, number_of_registers, registers.slice(0,2)...]
		var packet = [244, 53, 0, 0, 23, number_of_registers];
		for(let ind = 0; ind < number_of_registers; ind++){
			packet.push(...int2Bytes(registers[ind], 2));
		}
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_number_of_read_retries_539(sensor_mac, value){
		console.log('config_set_number_of_read_retries_539');
		var packet = [244, 55, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_read_parameter_539(sensor_mac, value){
		console.log('config_set_read_parameter_539');
		var packet = [244, 57, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_auto_raw_interval_110(sensor_mac, value){
		console.log('config_set_raw_interval_110');
		var packet = [244, 79, 0, 0, 80, 70, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_auto_raw_destination_110(sensor_mac, modem_mac){
		console.log('config_set_auto_raw_destination_110');
		var packet = [244, 79, 0, 0, 80, 72];
		let bytes = int2Bytes((modem_mac), 4);
		packet.push(...bytes);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_clear_probe_uptimers_110(sensor_mac){
		console.log('config_set_clear_probe_uptimers_110');
		var packet = [244, 79, 0, 0, 80, 75];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_smart_interval_110(sensor_mac, value){
		console.log('config_set_smart_interval_110');
		var packet = [244, 79, 0, 0, 101, 76, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_smart_threshold_110(sensor_mac, value){
		console.log('config_set_smart_threshold_110');
		var packet = [244, 79, 0, 0, 101, 78, 1, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_smart_threshold_p2_110(sensor_mac, value){
		console.log('config_set_smart_threshold_p2_110');
		var packet = [244, 79, 0, 0, 101, 78, 2, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_odr_p1_110(sensor_mac, value){
		console.log('config_set_odr_p1_110');
		var packet = [244, 79, 0, 0, 101, 0, 1, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_odr_p2_110(sensor_mac, value){
		console.log('config_set_odr_p2_110');
		var packet = [244, 79, 0, 0, 101, 0, 2, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sampling_duration_p1_110(sensor_mac, value){
		console.log('config_set_sampling_duration_p1_110');
		var packet = [244, 79, 0, 0, 101, 2, 1, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sampling_duration_p2_110(sensor_mac, value){
		console.log('config_set_sampling_duration_p2_110');
		var packet = [244, 79, 0, 0, 101, 2, 2, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_low_pass_filter_p1_110(sensor_mac, value){
		console.log('config_set_low_pass_filter_p1_110');
		var packet = [244, 79, 0, 0, 80, 52, 1, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_low_pass_filter_p2_110(sensor_mac, value){
		console.log('config_set_low_pass_filter_p2_110');
		var packet = [244, 79, 0, 0, 80, 52, 2, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_high_pass_filter_p1_110(sensor_mac, value){
		console.log('config_set_high_pass_filter_p1_110');
		var packet = [244, 79, 0, 0, 80, 54, 1, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_high_pass_filter_p2_110(sensor_mac, value){
		console.log('config_set_high_pass_filter_p2_110');
		var packet = [244, 79, 0, 0, 80, 54, 2, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_motion_detect_threshold_p1_110(sensor_mac, value){
		console.log('config_set_motion_detect_threshold_p1_110');
		var packet = [244, 79, 0, 0, 80, 60, 1, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_motion_detect_threshold_p2_110(sensor_mac, value){
		console.log('config_set_motion_detect_threshold_p2_110');
		var packet = [244, 79, 0, 0, 80, 60, 2, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_max_raw_sample_110(sensor_mac, value){
		console.log('config_set_max_raw_sample_110');
		var packet = [244, 79, 0, 0, 101, 87];
		var val = int2Bytes(value, 2);
		packet.push(...val);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_led_accelerometer_threshold_114(sensor_mac, value){
		console.log('config_set_led_accelerometer_threshold_114');
		var packet = [244, 79, 0, 0, 0, 62, 1, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_led_velocity_threshold_114(sensor_mac, value){
		console.log('config_set_led_velocity_threshold_114');
		var packet = [244, 79, 0, 0, 0, 64, 1, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_acceleration_interrupt_threshold_84(sensor_mac, value){
		console.log('config_set_acceleration_interrupt_threshold_84');
		var packet = [244, 79, 0, 0, 0, 60, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_current_calibration_13(sensor_mac, calib){
		console.log('config_set_current_calibration_13');
		var packet = [244, 1, 0, 0, 0];
		var cal_val = int2Bytes(calib, 4);
		packet.push(...cal_val);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_current_calibration_ch2_19(sensor_mac, calib){
		console.log('config_set_current_calibration_ch2_19');
		var packet = [244, 3, 0, 0, 0];
		var cal_val = int2Bytes(calib, 4);
		packet.push(...cal_val);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_current_calibration_ch3_28(sensor_mac, calib){
		console.log('cconfig_set_current_calibration_ch3_28');
		var packet = [244, 5, 0, 0, 0];
		var cal_val = int2Bytes(calib, 4);
		packet.push(...cal_val);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_current_calibration_13_dep(sensor_mac, calib){
		console.log('config_set_current_calibration_13_dep');
		var packet = [244, 1, 0, 0, 0];
		var cal_val = int2Bytes(calib, 2);
		packet.push(...cal_val);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_current_calibration_ch2_19_dep(sensor_mac, calib){
		console.log('config_set_current_calibration_ch2_19_dep');
		var packet = [244, 3, 0, 0, 0];
		var cal_val = int2Bytes(calib, 2);
		packet.push(...cal_val);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_current_calibration_ch3_28_dep(sensor_mac, calib){
		console.log('cconfig_set_current_calibration_ch3_28_dep');
		var packet = [244, 5, 0, 0, 0];
		var cal_val = int2Bytes(calib, 2);
		packet.push(...cal_val);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_rx485_timeout_1011(sensor_mac, value){
		console.log('config_set_Rx485_timeout_1011');
		var packet = [244, 48, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_mode_1011(sensor_mac, value){
		console.log('config_set_mode_1011');
		var packet = [244, 38, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_auto_address_timeout_1011(sensor_mac, value){
		console.log('config_set_auto_address_timeout_1011');
		var packet = [244, 40, 0, 0, 23];
		var time_val = int2Bytes(value, 2);
		packet.push(...time_val);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_stop_bit_1011(sensor_mac, value){
		console.log('config_set_stop_bit_1011');
		var packet = [244, 34, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_parity_1011(sensor_mac, value){
		console.log('config_set_parity_1011');
		var packet = [244, 33, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_reboot_1011(sensor_mac){
		console.log('config_set_reboot_1011');
		var packet = [247, 64, 0, 0, 0, 5, 22, 23];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_operation_mode_531(sensor_mac, mode){
		console.log('config_set_operation_mode_531');
		console.log(mode);
		var packet = [244, 32, 0, 0, 0, mode];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_calibration_58(sensor_mac){
		console.log('config_set_calibration_58');
		var packet = [244, 32, 0, 0, 23];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_factory_reset_tank_probe_58(sensor_mac){
		console.log('config_set_factory_reset_tank_probe_58');
		var packet = [244, 33, 0, 0, 23];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_max_range_58(sensor_mac, value){
		console.log('config_set_max_range_58');
		var packet = [244, 34, 0, 0, 23, 39, 16];
		var max = int2Bytes(value, 2);
		packet.push(...max);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	// config_set_clear_counter_33(sensor_mac){
	// 	console.log('config_clear_counter_33');
	// 	var packet = [244, 36, 0, 0, 0, 31];
	// 	console.log(packet);
	// 	return this.config_send(sensor_mac, packet);
	// }
	// config_set_push_notification_33(sensor_mac, value){
	// 	console.log('config_set_push_notification_33');
	// 	var packet = [244, 45, 0, 0, 0, value];
	// 	console.log(packet);
	// 	return this.config_send(sensor_mac, packet);
	// }
	config_set_change_otf_interval(sensor_mac, value){
		console.log('config_set_change_otf_interval');
		var packet = [244, 40, 0, 2, 5];
		var max = int2Bytes(value, 2);
		packet.push(...max);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sampling_rate_duration(sensor_mac, value){
		console.log('config_set_sampling_rate_duration');
		var packet = [244, 36, 0, 2, 5, 0, value, 255, 255];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_fly_interval_110(sensor_mac, value){
		console.log('config_set_fly_interval');
		var packet = [244, 79, 0, 0, 101, 81];
		let value_ = int2Bytes((value), 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_scd_skip_samples_44(sensor_mac, value){
		console.log('config_set_scd_skip_samples');
		var packet = [244, 34, 0, 0, 53, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sps_skip_samples_32(sensor_mac, value){
		console.log('config_set_scd_skip_samples');
		var packet = [244, 32, 0, 0, 53, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_rtd_type_39(sensor_mac, value){
		console.log('config_set_rtd_type_39');
		var packet = [244, 64, 0, 0, 39, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_rtd_range_39(sensor_mac, value){
		console.log('config_set_rtd_range_39');
		var packet = [244, 66, 0, 0, 39, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_pressure_sensor_fs_ch1_118(sensor_mac, value){
		console.log('config_set_pressure_sensor_fs_ch1_118');
		var packet = [244, 64, 0, 0, 26, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_pressure_sensor_fs_ch2_118(sensor_mac, value){
		console.log('config_set_pressure_sensor_fs_ch2_118');
		var packet = [244, 72, 0, 0, 118, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_auto_check_interval_118(sensor_mac, value){
		console.log('config_set_auto_check_interval_118');
		var packet = [244, 66, 0, 0, 26];
		let value_ = int2Bytes((value), 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_press_auto_check_percent_118(sensor_mac, value){
		console.log('config_set_press_auto_check_percent_118');
		var packet = [244, 68, 0, 0, 26, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_temp_auto_check_percent_118(sensor_mac, value){
		console.log('config_set_temp_auto_check_percent_118');
		var packet = [244, 70, 0, 0, 26, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_raw_length_97(sensor_mac, value){
		console.log('config_set_raw_length_97');
		var packet = [244, 67, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_raw_timeout_97(sensor_mac, value){
		console.log('config_set_raw_timeout_97');
		var packet = [244, 71, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_fly_rate_97(sensor_mac, value){
		console.log('config_set_fly_rate_97');
		var packet = [244, 73, 0, 0, 0];
		let value_ = int2Bytes((value), 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_boot_up_time_97(sensor_mac, value){
		console.log('config_set_boot_up_time_97');
		var packet = [244, 51, 0, 0, 0];
		let value_ = int2Bytes((value), 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_mode_97(sensor_mac, value){
		console.log('config_set_mode_97');
		var packet = [244, 83, 0, 0, 0, value]
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_pressure_sensor_type_21(sensor_mac, value){
		console.log('config_set_pressure_sensor_type_21');
		var packet = [244, 66, 0, 0, 21, value]
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_pressure_sensor_range_21(sensor_mac, value){
		console.log('config_set_pressure_sensor_range_21');
		var packet = [244, 64, 0, 0, 21, value]
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_thermocouple_type_112(sensor_mac, value){
		console.log('config_set_thermocouple_type_112');
		let packet = [244, 79, 0, 0, 101, 20, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_filter_thermocouple_112(sensor_mac, value){
		console.log('config_set_filter_thermocouple_112');
		let packet = [244, 79, 0, 0, 101, 22, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_cold_junction_thermocouple_112(sensor_mac, value){
		console.log('config_set_cold_junction_thermocouple_112');
		let packet = [244, 79, 0, 0, 101, 24, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sample_resolution_thermocouple_112(sensor_mac, value){
		console.log('config_set_sample_resolution_thermocouple_112');
		let packet = [244, 79, 0, 0, 101, 26, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_number_of_samples_thermocouple_112(sensor_mac, value){
		console.log('config_set_number_of_samples_thermocouple_112');
		let packet = [244, 79, 0, 0, 101, 28, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_operation_mode_thermocouple_112(sensor_mac, value){
		console.log('config_set_operation_mode_thermocouple_112');
		let packet = [244, 79, 0, 0, 101, 30, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_measurement_type_thermocouple_112(sensor_mac, value){
		console.log('config_set_number_of_samples_thermocouple_112');
		let packet = [244, 79, 0, 0, 101, 32, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_enable_rpm_calculate_status_110(sensor_mac, value){
		console.log('config_set_enable_rpm_calculate_status_110');
		let packet = [244, 79, 0, 0, 101, 85, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_enable_filtering_110(sensor_mac, value){
		console.log('config_set_enable_filtering_110');
		let packet = [244, 79, 0, 0, 101, 13, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_to_always_on_120(sensor_mac){
		console.log('config_set_to_always_on_120');
		let packet = [244, 10, 0, 0, 0, 1];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sensor_reset_120(sensor_mac){
		console.log('config_set_sensor_reset_120');
		let packet = [244, 12, 0, 0, 26];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sensor_calib_120(sensor_mac){
		console.log('config_set_sensor_calib_120');
		let packet = [244, 13, 0, 0, 74];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_alert_threshold_120(sensor_mac, value){
		console.log('config_set_alert_threshold_120');
		let packet = [244, 14, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_wood_type_121(sensor_mac, value){
		console.log('config_set_wood_type_121');
		let packet = [244, 10, 0, 0, 121, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_quality_of_service_121(sensor_mac, value){
		console.log('config_set_quality_of_service_121');
		var packet = [247, 52, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_tare_the_scale_217(sensor_mac){
		console.log('config_set_tare_the_scale_217');
		var packet = [244, 32, 0, 0, 26];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_weight_calib_217(sensor_mac, value){
		console.log('config_set_weight_calib_217');
		var packet = [244, 33, 0, 0, 26];
		let value_ = int2Bytes((value*100), 4);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_tare_value_217(sensor_mac, value){
		console.log('config_set_tare_value_217');
		var packet = [244, 34, 0, 0, 26];
		let value_ = int2Bytes((value*100), 4);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_pressure_limit_26(sensor_mac, value){
		console.log('config_set_pressure_limit_26');
		var packet = [244, 82, 0, 0, 26];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_auto_pressure_check_26(sensor_mac, value){
		console.log('config_set_auto_pressure_check_26');
		var packet = [244, 80, 0, 0, 26, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_fsr_420ma(sensor_mac, value){
		console.log('config_set_fsr_420ma');
		var packet = [244, 64, 0, 0, 45, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_always_on_420ma(sensor_mac, value){
		console.log('config_set_always_on_420ma');
		var packet = [244, 81, 0, 0, 88, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_motion_to_sampling_delay_110(sensor_mac, value){
		console.log('config_set_motion_to_sampling_delay_110');
		var packet = [244, 79, 0, 0, 101, 89, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_max_num_motion_tx_delay_110(sensor_mac, value){
		console.log('config_set_max_num_motion_tx_delay_110');
		var packet = [244, 79, 0, 0, 101, 91, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_enable_sensor_103(sensor_mac, value){
		console.log('config_set_enable_sensor_103');
		var packet = [244, 79, 0, 0, 101, 19, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_enable_hp_filter_cutoff_103(sensor_mac, value){
		console.log('config_set_enable_hp_filter_cutoff_103');
		var packet = [244, 79, 0, 0, 101, 17, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_gyro_fsr_103(sensor_mac, value){
		console.log('config_set_gyro_fsr_103');
		var packet = [244, 79, 0, 0, 101, 21, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_adxl_fsr_103(sensor_mac, value){
		console.log('config_set_adxl_fsr_103');
		var packet = [244, 79, 0, 0, 101, 11, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_acc_threshold_103(sensor_mac, value){
		console.log('config_set_acc_threshold_103');
		var packet = [244, 79, 0, 0, 101, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	// config_set_do_read_rate_270(sensor_mac, value){
	// 	console.log('config_set_do_report_rate_270');
	// 	var packet = [244, 32, 0, 0, 0, value];
	// 	console.log(packet);
	// 	return this.config_send(sensor_mac, packet);
	// }
	config_set_do_boot_time_270(sensor_mac, value){
		console.log('config_set_do_boot_time_270');
		var packet = [244, 34, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_do_dev_id_270(sensor_mac, id_1, id_2, id_3, id_4){
		console.log('config_set_do_dev_id_270');
		var packet = [244, 38, 0, 0, 0, id_1, id_2, id_3, id_4];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_ec_boot_time_270(sensor_mac, value){
		console.log('config_set_ec_boot_time_270');
		var packet = [244, 36, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_ec_dev_id_270(sensor_mac, id_1, id_2, id_3, id_4){
		console.log('config_set_ec_dev_id_270');
		var packet = [244, 40, 0, 0, 0, id_1, id_2, id_3, id_4];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_rate_211(sensor_mac, value){
		console.log('config_set_oxygen_rate_211');
		var packet = [244, 32, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_timeout_211(sensor_mac, value){
		console.log('config_set_oxygen_timeout_211');
		var packet = [244, 37, 0, 0, 0];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_threshold_211(sensor_mac, value){
		console.log('config_set_oxygen_threshold_211');
		var packet = [244, 34, 0, 0, 0];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_interrupt_timeout_108(sensor_mac, value){
		console.log('config_set_interrupt_timeout_108');
		var packet = [244, 65, 0, 0, 0];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_start_sps30_fan_cleaning_53(sensor_mac){
		console.log('config_set_start_sps30_fan_cleaning_53');
		var packet = [244, 26, 0, 0, 53];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_max_flow_541(sensor_mac, value){
		console.log('config_set_max_flow_541');
		var packet = [244, 85, 0, 0, 88, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_min_flow_541(sensor_mac, value){
		console.log('config_set_min_flow_541');
		var packet = [244, 83, 0, 0, 88, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_ct_constant_87(sensor_mac, value){
		console.log('config_set_ct_constant_87');
		var packet = [244, 80, 0, 0, 0, 1];
		let value_ = int2Bytes(value, 4);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_deadband_87(sensor_mac, value){
		console.log('config_set_deadband_87');
		var packet = [244, 82, 0, 0, 0, 1];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sampling_frequency_87(sensor_mac, value){
		console.log('config_set_sampling_frequency_87');
		var packet = [244, 78, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_raw_length_87(sensor_mac, value){
		console.log('config_set_raw_length_87');
		var packet = [244, 84, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_volume_flow_unit_124(sensor_mac, value){
		console.log('config_set_volume_flow_unit_124');
		var packet = [244, 51, 0, 0, 124, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_total_1_flow_unit_124(sensor_mac, value){
		console.log('config_set_total_1_flow_unit_124');
		var packet = [244, 52, 0, 0, 124, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_total_2_flow_unit_124(sensor_mac, value){
		console.log('config_set_total_2_flow_unit_124');
		var packet = [244, 53, 0, 0, 124, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_total_3_flow_unit_124(sensor_mac, value){
		console.log('config_set_total_3_flow_unit_124');
		var packet = [244, 54, 0, 0, 124, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_pressure_unit_124(sensor_mac, value){
		console.log('config_set_pressure_unit_124');
		var packet = [244, 55, 0, 0, 124, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_total_1_op_mode_124(sensor_mac, value){
		console.log('config_set_total_1_op_mode_124');
		var packet = [244, 48, 0, 0, 124, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_total_2_op_mode_124(sensor_mac, value){
		console.log('config_set_total_2_op_mode_124');
		var packet = [244, 49, 0, 0, 124, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_total_3_op_mode_124(sensor_mac, value){
		console.log('config_set_total_3_op_mode_124');
		var packet = [244, 50, 0, 0, 124, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_conductivity_unit_124(sensor_mac, value){
		console.log('config_set_conductivity_unit_124');
		var packet = [244, 56, 0, 0, 124, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_reset_all_totalizers_124(sensor_mac, value){
		console.log('config_set_reset_all_totalizers_124');
		var packet = [244, 64, 0, 0, 124, 1];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_screen_control_108(sensor_mac, value){
		console.log('config_set_screen_control_108');
		var packet = [244, 69, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_screen_on_time_108(sensor_mac, value){
		console.log('config_set_screen_on_time_108');
		var packet = [244, 67, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_boot_time_536(sensor_mac, value){
		console.log('config_set_oxygen_boot_time_536');
		var packet = [244, 41, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_flow_boot_time_536(sensor_mac, value){
		console.log('config_set_flow_boot_time_536');
		var packet = [244, 49, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_dev_addr_536(sensor_mac, id_1, id_2, id_3, id_4){
		console.log('config_set_oxygen_dev_addr_536');
		var packet = [244, 51, 0, 0, 0, id_1, id_2, id_3, id_4];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_flow_dev_addr_536(sensor_mac, id_1, id_2, id_3, id_4){
		console.log('config_set_flow_dev_addr_536');
		var packet = [244, 53, 0, 0, 0, id_1, id_2, id_3, id_4];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	// config_set_oxygen_rate_536(sensor_mac, value){
	// 	console.log('config_set_oxygen_rate_536');
	// 	var packet = [244, 32, 0, 0, 0, value];
	// 	console.log(packet);
	// 	return this.config_send(sensor_mac, packet);
	// }
	// config_set_oxygen_timeout_536(sensor_mac, value){
	// 	console.log('config_set_oxygen_timeout_536');
	// 	var packet = [244, 37, 0, 0, 0];
	// 	let value_ = int2Bytes(value, 2);
	// 	packet.push(...value_);
	// 	console.log(packet);
	// 	return this.config_send(sensor_mac, packet);
	// }
	config_set_oxygen_max_threshold_s1_536(sensor_mac, value){
		console.log('config_set_oxygen_max_threshold_s1_536');
		//value = value * 100;
		var packet = [244, 34, 0, 0, 0, 1];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_max_threshold_s2_536(sensor_mac, value){
		console.log('config_set_oxygen_max_threshold_s2_536');
		//value = value * 100;
		var packet = [244, 34, 0, 0, 0, 2];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_max_threshold_s3_536(sensor_mac, value){
		//value = value * 100;
		console.log('config_set_oxygen_max_threshold_s3_536');
		var packet = [244, 34, 0, 0, 0, 3];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_max_threshold_s4_536(sensor_mac, value){
		//value = value * 100;
		console.log('config_set_oxygen_max_threshold_s4_536');
		var packet = [244, 34, 0, 0, 0, 4];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_min_threshold_s1_536(sensor_mac, value){
		console.log('config_set_oxygen_min_threshold_s1_536');
		//value = value * 100;
		var packet = [244, 39, 0, 0, 0, 1];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_min_threshold_s2_536(sensor_mac, value){
		console.log('config_set_oxygen_min_threshold_s2_536');
		//value = value * 100;
		var packet = [244, 39, 0, 0, 0, 2];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_min_threshold_s3_536(sensor_mac, value){
		//value = value * 100;
		console.log('config_set_oxygen_min_threshold_s3_536');
		var packet = [244, 39, 0, 0, 0, 3];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_min_threshold_s4_536(sensor_mac, value){
		//value = value * 100;
		console.log('config_set_oxygen_min_threshold_s4_536');
		var packet = [244, 39, 0, 0, 0, 4];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_axis_enabled_119(sensor_mac, value){
		console.log('config_set_axis_enabled_119');
		var packet = [244, 73, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_raw_acc_data_119(sensor_mac, value){
		console.log('config_set_raw_acc_data_119');
		var packet = [244, 71, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_sensor_length_29(sensor_mac, value){
		console.log('config_set_sensor_length_29');
		var packet = [244, 63, 0, 0, 29];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_rtd_wire_type_ch2_54(sensor_mac, value){
		console.log('config_set_rtd_wire_type_ch2_54');
		var packet = [244, 68, 0, 0, 39, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_rtd_range_ch2_54(sensor_mac, value){
		console.log('config_set_rtd_range_ch2_54');
		var packet = [244, 70, 0, 0, 39, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_rtd_wire_type_ch3_55(sensor_mac, value){
		console.log('config_set_rtd_wire_type_ch3_55');
		var packet = [244, 72, 0, 0, 39, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_rtd_range_ch3_55(sensor_mac, value){
		console.log('config_set_rtd_range_ch3_55');
		var packet = [244, 74, 0, 0, 39, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_max_num_motion_103(sensor_mac, value){
		console.log('config_set_max_num_motion_103');
		let packet = [244, 79, 0, 0, 101, 33, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_acc_output_data_rate_103(sensor_mac, value){
		console.log('config_set_acc_output_data_rate_103');
		let packet = [244, 79, 0, 0, 101, 25, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_temperature_unit_545(sensor_mac, value){
		console.log('config_set_temperature_unit_545');
		let packet = [244, 37, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_flow_unit_545(sensor_mac, value){
		console.log('config_set_flow_unit_545');
		let packet = [244, 38, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_gas_type_545(sensor_mac, value){
		console.log('config_set_gas_type_545');
		let packet = [244, 34, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_gas_type_mix_545(sensor_mac, num, gas_type, percentage){
		console.log('config_set_gas_type_mix_545');
		var packet = [244, 35, 0, 0, 0];
		for(let index = 0; index < num; index++){
			packet.push(...int2Bytes(gas_type[index], 1));
			packet.push(...int2Bytes(percentage[index]*100, 2));
		}
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_screen_on_time_217(sensor_mac, value){
		console.log('config_set_screen_on_time_217');
		let packet = [244, 40, 0, 0, 26, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_weight_unit_217(sensor_mac, value){
		console.log('config_set_weight_unit_217');
		let packet = [244, 38, 0, 0, 26, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_clean_cycle_99(sensor_mac, value){
		console.log('config_set_clean_cycle_99');
		let packet = [244, 34, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_wiper_rounds_number_99(sensor_mac, value){
		console.log('config_set_wiper_rounds_number_99');
		let packet = [244, 36, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_distance_under_strength_threshold_99(sensor_mac, distance, threshold){
		console.log('config_set_distance_under_strength_threshold_99');
		let packet = [244, 25, 0, 0, 0];
		let distance_ = int2Bytes(distance, 2);
		packet.push(...distance_);
		let threshold_ = int2Bytes(threshold, 2);
		packet.push(...threshold_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_clean_sensor_99(sensor_mac){
		console.log('config_set_clean_sensor_99');
		let packet = [244, 40, 0, 0, 0];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_reset_sensor_99(sensor_mac){
		console.log('config_set_reset_sensor_99');
		let packet = [244, 38, 0, 0, 0];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_factory_reset_sensor_99(sensor_mac){
		console.log('config_set_factory_reset_sensor_99');
		let packet = [244, 39, 0, 0, 0];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_calibration_factor_217(sensor_mac, value){
		console.log('config_set_calibration_factor_217');
		var packet = [244, 48, 0, 0, 0];
		let value_ = int2Bytes(value, 4);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_clear_timers_35(sensor_mac){
		console.log('config_clear_timers_35');
		var packet = [244, 36, 0, 0, 0, 1];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_probe_one_ct_126(sensor_mac, value){
		console.log('config_set_probe_one_ct_126');
		let packet = [244, 67, 0, 0, 0, 1, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_probe_two_ct_126(sensor_mac, value){
		console.log('config_set_probe_two_ct_126');
		let packet = [244, 67, 0, 0, 0, 2, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_probe_three_ct_126(sensor_mac, value){
		console.log('config_set_probe_three_ct_126');
		let packet = [244, 67, 0, 0, 0, 3, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_probe_one_current_threshold_126(sensor_mac, value){
		console.log('config_set_probe_one_current_threshold_126');
		var packet = [244, 69, 0, 0, 0, 1];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_probe_two_current_threshold_126(sensor_mac, value){
		console.log('config_set_probe_two_current_threshold_126');
		var packet = [244, 69, 0, 0, 0, 2];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_probe_three_current_threshold_126(sensor_mac, value){
		console.log('config_set_probe_three_current_threshold_126');
		var packet = [244, 69, 0, 0, 0, 3];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_send_raw_on_motion_only_103(sensor_mac, value){
		console.log('config_set_send_raw_on_motion_only_103');
		var packet = [244, 79, 0, 0, 101, 35, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_adc_threshold_128(sensor_mac, value){
		console.log('config_set_adc_threshold_128');
		var packet = [244, 32, 0, 0, 21];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_auto_check_interval_128(sensor_mac, value){
		console.log('config_set_auto_check_interval_128');
		var packet = [244, 34, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_auto_calibration_128(sensor_mac){
		console.log('config_set_auto_calibration_128');
		var packet = [244, 33, 0, 0, 0, 1];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_reset_total_545(sensor_mac){
		console.log('config_set_reset_total_545');
		var packet = [244, 36, 0, 0, 0];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_cal_validation_545(sensor_mac){
		console.log('config_set_cal_validation_545');
		var packet = [244, 39, 0, 0, 0];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_boot_time_4(sensor_mac, value){
		console.log('config_set_boot_time_4');
		var packet = [244, 97, 0, 0, 23];
		let value_ = int2Bytes(value, 2);
		packet.push(...value_);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_operation_mode_4(sensor_mac, value){
		console.log('config_set_operation_mode_4');
		var packet = [244, 93, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_lube_on_time_1_105(sensor_mac, value){
		console.log('config_set_lube_on_time_1_105');
		var packet = [244, 87, 0, 0, 88, 1, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_lube_on_time_2_105(sensor_mac, value){
		console.log('config_set_lube_on_time_2_105');
		var packet = [244, 87, 0, 0, 88, 2, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_faulty_level_105(sensor_mac, value){
		console.log('config_set_faulty_level_105');
		var packet = [244, 83, 0, 0, 88, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_friction_level_105(sensor_mac, value){
		console.log('config_set_friction_level_105');
		var packet = [244, 85, 0, 0, 88, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_vibration_mode_115(sensor_mac, value){
		console.log('config_set_vibration_mode_115');
		var packet = [244, 32, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_offset_mode_115(sensor_mac, value){
		console.log('config_set_offset_mode_115');
		var packet = [244, 34, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_flow_sensor_baud_rate_536(sensor_mac, value){
		console.log('config_set_flow_sensor_baud_rate_536');
		var packet = [244, 55, 0, 0, 0];
		let baudrate = int2Bytes((value), 4);
		packet.push(...baudrate);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_sensor_baud_rate_536(sensor_mac, value){
		console.log('config_set_oxygen_sensor_baud_rate_536');
		var packet = [244, 61, 0, 0, 0];
		let baudrate = int2Bytes((value), 4);
		packet.push(...baudrate);
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_flow_sensor_parity_bits_536(sensor_mac, value){
		console.log('config_set_flow_sensor_parity_bits_536');
		var packet = [244, 57, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_sensor_parity_bits_536(sensor_mac, value){
		console.log('config_set_oxygen_sensor_parity_bits_536');
		var packet = [244, 63, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_flow_sensor_stop_bits_536(sensor_mac, value){
		console.log('config_set_flow_sensor_stop_bits_536');
		var packet = [244, 59, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_oxygen_sensor_stop_bits_536(sensor_mac, value){
		console.log('config_set_oxygen_sensor_stop_bits_536');
		var packet = [244, 65, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_tilt_mode_47(sensor_mac, value){
		console.log('config_set_tilt_mode_47');
		var packet = [244, 34, 0, 0 ,0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_tilt_angle_47(sensor_mac, value){
		console.log('config_set_tilt_angle_47');
		var packet = [244, 32, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_tilt_timer_47(sensor_mac, value){
		console.log('config_set_tilt_timer_47');
		var packet = [244, 36, 0, 0, 0, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_set_stop_bit_539(sensor_mac, value){
		console.log('config_set_stop_bit_539');
		var packet = [244, 34, 0, 0, 23, value];
		console.log(packet);
		return this.config_send(sensor_mac, packet);
	}
	config_get_delay(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [247, 21, 0, 0, 0]).then((res) => {
				fulfill({
					nodeId: res.nodeId,
					delay: res.data.slice(0, 3).reduce(msbLsb)
				});
			}).catch(reject);
		});
	}
	config_get_power(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [247, 22, 0, 0, 0]).then((res) => {
				fulfill(res.data[0]);
			}).catch(reject);
		});
	}
	config_get_retries(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [247, 23, 0, 0, 0]).then((res) => {
				fulfill(res.data[0]);
			}).catch(reject);
		});
	}
	config_get_destination(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [247, 24, 0, 0, 0]).then((res) => {
				fulfill(toMac(res.data.slice(0, 4)));
			}).catch(reject);
		});
	}
	config_get_pan_id(sensor_mac, node_id, sensor_type){

		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [247, 25, 0, 0, 0]).then((res) => {
				fulfill(res.data.slice(0, 2).reduce(msbLsb));
			}).catch(reject);
		});
	}
	config_get_change_detection(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [247, 26, 0, 0, 0]).then((res) => {
				fulfill({
					enabled: res[0],
					threshold: res[1],
					interval: res.data.slice(2, 5).reduce(msbLsb)
				});
			}).catch(reject);
		});
	}
	config_get_cm_calibration(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [244, 2, 0, 0, 0]).then((res) => {
				fulfill(res.data.slice(0, 2).reduce(msbLsb) / 100);
			}).catch(reject);
		});
	}
	config_get_bp_altitude(sensor_mac){
		this.config_send(sensor_mac, [244, 5, 0, 0, 0]).then((res) => {
			fulfill(res.data.slice(0, 2).reduce(msbLsb));
		}).catch(reject);
	}
	config_get_bp_pressure(sensor_mac){
		this.config_send(sensor_mac, [244, 8, 0, 0, 0]).then((res) => {
			fulfill(res.data.slice(0, 2).reduce(msbLsb));
		}).catch(reject);
	}
	config_get_bp_temp_precision(sensor_mac){
		return this.config_send(sensor_mac, [244, 6, 0, 0, 0]);
	}
	config_get_bp_press_precision(sensor_mac){
		return this.config_send(sensor_mac, [244, 7, 0, 0, 0]);
	}
	config_get_amgt_accel(sensor_mac){
		return this.config_send(sensor_mac, [244, 4, 0, 0, 0]);
	}
	config_get_amgt_magnet(sensor_mac){
		return this.config_send(sensor_mac, [244, 5, 0, 0, 0]);
	}
	config_get_amgt_gyro(sensor_mac){
		return this.config_send(sensor_mac, [244, 6, 0, 0, 0]);
	}
	config_get_impact_accel(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [244, 5, 0, 0, 0]).then((res) => {
				fulfill(res.data[0]);
			}).catch(reject);
		});
	}
	config_get_impact_data_rate(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [244, 6, 0, 0, 0]).then((res) => {
				fulfill(res.data[0]);
			}).catch(reject);
		});
	}
	config_get_impact_threshold(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [244, 7, 0, 0, 0]).then((res) => {
				fulfill(res.data[0]);
			}).catch(reject);
		});
	}
	config_get_impact_duration(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [244, 8, 0, 0, 0]).then((res) => {
				fulfill(res.data[0]);
			}).catch(reject);
		});
	}
	config_get_activ_interr(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [244, 10, 0, 0, 0]).then((res) => {
				fulfill(res.data[0]);
			}).catch(reject);
		});
	}
	config_get_filtering(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [244, 5, 0, 0, 0]).then((res) => {
				fulfill(res.data[0]);
			}).catch(reject);
		});
	}
	config_get_data_rate(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [244, 6, 0, 0, 0]).then((res) => {
				fulfill(res.data[0]);
			}).catch(reject);
		});
	}
	config_get_time_series(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [244, 9, 0, 0, 0]).then((res) => {
				fulfill(res.data[0]);
			}).catch(reject);
		});
	}
	config_get_reading_type(sensor_mac){
		return new Promise((fulfill, reject) => {
			this.config_send(sensor_mac, [244, 7, 0, 0, 0]).then((res) => {
				fulfill(res.data[0]);
			}).catch(reject);
		});
	}
	config_enable_encryption(sensor_mac){
		return this.config_send(sensor_mac, [242, 1, 0, 0, 0]);
	}
	config_enter_otn_mode(sensor_mac){
		console.log('config_enter_otn_mode');
		// F4 4F 00 00 65 32
		// This command is used for OTF on types 53, 80,81,82,83,84, 101, 102 , 518,519
		return this.config_send(sensor_mac, [244, 79, 0, 0, 101, 50]);
		// return this.config_send('00:00:00:00:00:00:FF:FF', [244, 79, 0, 0, 101, 50]);
	}
	config_exit_otn_mode(sensor_mac){
		console.log('config_exit_otn_mode');
		// F4 4F 00 00 65 33
		// This command is used for OTF on types 53, 80,81,82,83,84, 101, 102 , 518,519
		return this.config_send(sensor_mac, [244, 79, 0, 0, 101, 51]);
		// return this.config_send('00:00:00:00:00:00:FF:FF', [244, 79, 0, 0, 101, 50]);
	}
	config_enter_otn_mode_common(sensor_mac){
		console.log('config_enter_otn_mode_common');
		return this.config_send(sensor_mac, [247, 54, 0, 0, 0]);
		// return this.config_send('00:00:00:00:00:00:FF:FF', [244, 79, 0, 0, 101, 50]);
	}
	config_exit_otn_mode_common(sensor_mac){
		console.log('config_exit_otn_mode_common');
		return this.config_send(sensor_mac, [247, 55, 0, 0, 0]);
		// return this.config_send('00:00:00:00:00:00:FF:FF', [244, 79, 0, 0, 101, 50]);
	}
	config_disable_encryption(sensor_mac){
		return this.config_send(sensor_mac, [242, 2, 0, 0, 0]);
	}
	config_set_encryption(sensor_mac, ...key){
		if(key[0].constructor == Array) key = key[0];
		var packet = [242, 1];
		packet.push(...key);
		return this.config_send(sensor_mac, packet);
	}
	control_start_luber(sensor_mac, luber = 1, duration){
		console.log('control_start_luber');
		let packet = [244, 78, 0, 0, 45, luber, duration];
		console.log(packet);
		let cmd_timeout = (duration * 1000) + 1000;
		return this.config_send(sensor_mac, packet, {}, cmd_timeout);
	}
	config_powered_device(sensor_mac, param, ...data){
		var params = {
			destination: 0,
			network_id: 1,
			power: 2,
			retries: 3,
			node_id: 4,
			delay: 5
		};
		return this.config_send(sensor_mac, [(data ? 247 : 248), params[param], ...data]);
	}
	clear_queue(){
		this.queue.queue = new Array;
		delete this.queue;
		this.queue = new Queue(1);
	}
	firmware_send_chunk_v13(sensor_mac, offset, chunk){
		// console.log('firmware_send_chunk');
		// sensor_mac = "00:00:00:00:00:00:ff:ff";
		let packet = [245, 59, 0, 0, 0].concat(offset, Array.prototype.slice.call(chunk));
		// console.log(packet);
		return this.firmware_send_v13(sensor_mac, packet);
	}
	firmware_read_last_chunk_segment(sensor_mac){
		// console.log('firmware_read_last_chunk_segment');
		let packet = [245, 61, 0, 0, 0];
		return this.config_send(sensor_mac, packet);
	}
	clear_queue_except_last(){
		const pending = this.queue.pendingPromises;
		const temp_queue = this.queue.queue;
		const last_promise = [temp_queue.pop()];

		console.log('MARK 1');
		console.log(last_promise);

		// this.queue.queue = last_promise;
		// this.queue.pendingPromises = [];
		// this.queue.activeCount = 0;

		console.log('MARK 2');
		console.log(pending);

		// console.log(temp_queue);
		// console.log(temp_queue[0]);
		// while(this.queue.queue.length > 1){
		// 	let clear_promise = this.queue.queue.shift();
		// 	clear_promise.reject({
		// 		res: 'Promise rejected by queue clearing',
		// 		error: 'Promise rejected by queue clearing'
		// 	});
		// };
		// temp_queue.forEach(promise => {
		// 	// promise.reject(new Error('Promise rejected by queue clearing'));
		// 	promise.reject({
		// 		res: 'Promise rejected by queue clearing',
		// 		error: 'Promise rejected by queue clearing'
		// 	});
		// });
	}
	firmware_send_v13(sensor_mac, data, opts, cmd_timeout = 7000, cmd_delay = 140, manifest = false){
		var that = this;
		return new Promise((fulfill, reject) => {
			if(manifest){
				that.queue.add(() => {
					return new Promise((f, r) => {
						setTimeout(f, 500);
					});
				});
			};
			that.queue.add(() => {
				return new Promise((f, r) => {
					var tout;
					function fail(error){
						console.log('In Fail');
						// that._emitter.removeListener('config_ack-'+sensor_mac, pass);
						clearTimeout(tout);
						reject({
							error: error,
							sent: [mac2bytes(sensor_mac), data, opts]
						});
						f();
					}
					function pass(packet){
						clearTimeout(tout);
						// that._emitter.removeListener('config_error-'+sensor_mac, fail);
						packet.sent = data;
						fulfill(packet);
						f();
					};
					let retry_count = 0;
					const max_retries = 3;
					function attempt_transmission(){
						that.send.transmit_request(mac2bytes(sensor_mac), data, opts).then(function(frame){
							if(frame.hasError){
								console.log('FRAME FAILED DUE TO FAIL FLAG');
								if(retry_count < max_retries) {
									retry_count++;
									console.log(`Retrying (attempt ${retry_count}) due to failed status`);
									clearTimeout(tout);
									startTimeout();
									attempt_transmission();
								}else{
									fail('Transmit Status indicated error');
									// reject({
									// 	error: 'Transmit Status indicated error',
									// 	sent: [mac2bytes(sensor_mac), data, opts]
									// });
								};
							}else{
								pass(frame);
							}
						}).catch((err) => {
							if(retry_count < max_retries) {
								retry_count++;
								console.log(`Retrying (attempt ${retry_count}) due to failed status`);
								attempt_transmission();
							}else{
								console.log('In CATCH');
								// reject({
								// 	error: err,
								// 	sent: [mac2bytes(sensor_mac), data, opts]
								// });
								fail(err);
							}
						}).then();
					}
					// that._emitter.once('config_ack-'+sensor_mac, pass);
					// that._emitter.once('config_error-'+sensor_mac, fail);
					function startTimeout(){
						tout = setTimeout(() => {
							console.log('In Timeout');
							fail('Transmit Request Timed Out');
						}, cmd_timeout);
					}
					attempt_transmission();
					startTimeout();
				});
			});
			this.queue.add(() => {
				return new Promise((f, r) => {
					// NOTE: This timeout is required to allow the sensor to process and write to memory before a new chunk is transmitted.
					setTimeout(f, cmd_delay);
				});
			});
		});
	};
	config_send_sync(sensor_mac, data, opts, cmd_timeout = 20000, cmd_pretimeout = 0){
		console.log('-------------------config_send_sync');
		var that = this;
		return new Promise((fulfill, reject) => {
			// Note: This code adds a delay before sending any communications. This is not generally required, but has shown increased reliability
			// in some cases when used directly after changing modem module settings such as entering FON network ID. Leaving it here for future reference or quick add during support.
			// consider adding a pre-time command parameter to allow this as in the v13 firmware send function.
			if(cmd_pretimeout != 0){
				that.queue.add(() => {
					return new Promise((f, r) => {
						setTimeout(f, cmd_pretimeout);
					});
				});
			};
			that.queue.add(() => {
				return new Promise((f, r) => {
					var tout;
					function fail(packet){
						that._emitter.removeListener('sync_acknowledgment-'+sensor_mac, pass);
						clearTimeout(tout);
						reject({
							err: packet,
							sent: [mac2bytes(sensor_mac), data, opts]
						});
						f();
					}
					function pass(packet){
						clearTimeout(tout);
						that._emitter.removeListener('sync_acknowledgment_error-'+sensor_mac, fail);
						packet.sent = [mac2bytes(sensor_mac), data, opts];
						fulfill(packet);
						f(packet);
					};

					that._emitter.once('sync_acknowledgment-'+sensor_mac, pass);
					that._emitter.once('sync_acknowledgment_error-'+sensor_mac, fail);
					tout = setTimeout(() => {
						that._emitter.removeListener('sync_acknowledgment_error-'+sensor_mac, fail);
						that._emitter.removeListener('sync_acknowledgment-'+sensor_mac, pass);
						//console.log(data, packet);
						if(sensor_mac == '00:00:00:00:00:00:FF:FF'){
							reject({
								res: 'Broadcast mode, no target device',
								sent: [mac2bytes(sensor_mac), data, opts]
							});
						}else{
							reject({
								err: 'No config err or ack, timeout',
								sent: [mac2bytes(sensor_mac), data, opts]
							});
						}

						f();
					}, cmd_timeout);
					that.send.transmit_request(mac2bytes(sensor_mac), data, opts).then().catch((err) => {
						that._emitter.removeListener('sync_acknowledgment_error-'+sensor_mac, fail);
						that._emitter.removeListener('sync_acknowledgment-'+sensor_mac, pass);
						reject({
							err: err,
							sent: [mac2bytes(sensor_mac), data, opts]
						});
						f();
					}).then();
				});
			});
			this.queue.add(() => {
				return new Promise((f, r) => {
					setTimeout(f, 250);
				});
			});
		});
	};
	config_send(sensor_mac, data, opts, cmd_timeout = 1500, cmd_pretimeout = 0){
		var that = this;
		return new Promise((fulfill, reject) => {
			// Note: This code adds a delay before sending any communications. This is not generally required, but has shown increased reliability
			// in some cases when used directly after changing modem module settings such as entering FON network ID. Leaving it here for future reference or quick add during support.
			// consider adding a pre-time command parameter to allow this as in the v13 firmware send function.
			if(cmd_pretimeout != 0){
				that.queue.add(() => {
					return new Promise((f, r) => {
						setTimeout(f, cmd_pretimeout);
					});
				});
			};
			that.queue.add(() => {
				return new Promise((f, r) => {
					var tout;
					function fail(packet){
						that._emitter.removeListener('config_ack-'+sensor_mac, pass);
						clearTimeout(tout);
						reject({
							err: packet,
							sent: [mac2bytes(sensor_mac), data, opts]
						});
						f();
					}
					function pass(packet){
						clearTimeout(tout);
						that._emitter.removeListener('config_error-'+sensor_mac, fail);
						packet.sent = data;
						fulfill(packet);
						f();
					};

					that._emitter.once('config_ack-'+sensor_mac, pass);
					that._emitter.once('config_error-'+sensor_mac, fail);
					tout = setTimeout(() => {
						that._emitter.removeListener('config_error-'+sensor_mac, fail);
						that._emitter.removeListener('config_ack-'+sensor_mac, pass);
						//console.log(data, packet);
						if(sensor_mac == '00:00:00:00:00:00:FF:FF'){
							reject({
								res: 'Broadcast mode, no target device',
								sent: [mac2bytes(sensor_mac), data, opts]
							});
						}else{
							reject({
								err: 'No config err or ack, timeout',
								sent: [mac2bytes(sensor_mac), data, opts]
							});
						}

						f();
					}, cmd_timeout);
					that.send.transmit_request(mac2bytes(sensor_mac), data, opts).then().catch((err) => {
						that._emitter.removeListener('config_error-'+sensor_mac, fail);
						that._emitter.removeListener('config_ack-'+sensor_mac, pass);
						reject({
							err: err,
							sent: [mac2bytes(sensor_mac), data, opts]
						});
						f();
					}).then();
				});
			});
			this.queue.add(() => {
				return new Promise((f, r) => {
					setTimeout(f, 250);
				});
			});
		});
	};
	prepare_bridge_query(sensor_mac, commands){
		commands.forEach((command) => {
			if(command.hasOwnProperty('meta')){
				this.queue_bridge_query(sensor_mac, command.command, command.meta);
			}else{
				this.queue_bridge_query(sensor_mac, command.command);
			}
		});
	}
	queue_bridge_query(sensor_mac, data, meta = null, command_timeout = 1500, opts){
		console.log('Command Timeout:');
		console.log(command_timeout);
		var that = this;
		if(!globalDevices.hasOwnProperty(sensor_mac)){
			globalDevices[sensor_mac] = {bridge: true, command_queue: []};
		}
		// if(!Object.hasOwn(node._gateway_node.sensor_list[msg.payload.address], 'command_queue')){
		// if(!globalDevices[sensor_mac].hasOwnProperty('command_queue')){
		// 	globalDevices[sensor_mac].command_queue = [{'command': data, 'meta': meta}];
		// }else{
		globalDevices[sensor_mac].command_queue.push({'command': data, 'meta': meta});
		// }

		return new Promise((fulfill, reject) => {
			that.queue.add(() => {
				return new Promise((f, r) => {
					var tout;
					function fail(response){
						that._emitter.removeListener('converter_ack-'+sensor_mac, pass);
						clearTimeout(tout);

						if(globalDevices[sensor_mac].hasOwnProperty('command_queue')){
							globalDevices[sensor_mac].command_queue.shift();
						};
						// if(globalDevices[sensor_mac].command_queue.length == 0 && globalDevices[sensor_mac].hasOwnProperty('bridge')){
						// 	delete globalDevices[sensor_mac].bridge;
						// }
						reject({
							err: response,
							sent: [mac2bytes(sensor_mac), data, opts]
						});
						f();
					}
					function pass(response){
						clearTimeout(tout);

						that._emitter.emit('converter_response', response);
						that._emitter.emit('converter_response-'+sensor_mac, response);


						if(globalDevices[sensor_mac].hasOwnProperty('command_queue')){
							globalDevices[sensor_mac].command_queue.shift();
						}
						// if(globalDevices[sensor_mac].command_queue.length == 0 && globalDevices[sensor_mac].hasOwnProperty('bridge')){
						// 	delete globalDevices[sensor_mac].bridge;
						// }
						that._emitter.removeListener('converter_error-'+sensor_mac, fail);
						fulfill(response);
						f();
						// that._emitter.
					};

					that._emitter.once('converter_ack-'+sensor_mac, pass);
					that._emitter.once('converter_error-'+sensor_mac, fail);
					tout = setTimeout(() => {
						that._emitter.removeListener('converter_error-'+sensor_mac, fail);
						that._emitter.removeListener('converter_ack-'+sensor_mac, pass);

						if(globalDevices[sensor_mac].hasOwnProperty('command_queue')){
							globalDevices[sensor_mac].command_queue.shift();
						};
						if(sensor_mac == '00:00:00:00:00:00:FF:FF'){
							reject({
								res: 'Broadcast mode, no target device',
								sent: [mac2bytes(sensor_mac), data, opts]
							});
						}else{
							reject({
								err: 'Wireless Converter did not respond',
								sent: [mac2bytes(sensor_mac), data, opts]
							});
						};

						f();
					}, command_timeout);
					that.send.transmit_request(mac2bytes(sensor_mac), data, opts).then().catch((err) => {
						that._emitter.removeListener('converter_error-'+sensor_mac, fail);
						that._emitter.removeListener('converter_ack-'+sensor_mac, pass);
						reject({
							err: err,
							sent: [mac2bytes(sensor_mac), data, opts]
						});
						f();
					}).then();
				});
			});
			this.queue.add(() => {
				return new Promise((f, r) => {
					setTimeout(f, 500);
				});
			});
		});
	};
	prepare_bridge_query(sensor_mac, commands, timeout){
		commands.forEach((command) => {
			if(command.hasOwnProperty('meta')){
				this.queue_bridge_query(sensor_mac, command.command, command.meta, timeout);
			}else{
				this.queue_bridge_query(sensor_mac, command.command, null, timeout);
			};
		});
	};
	build_102_data(payload, deviceAddr, hour, minute, sdata_start, current_packet, firmware){
		if(current_packet != 1){
			console.log('bad packet cleanup');
			return;
		}
		// var odr;
		var odr = payload[8];
		var device_temp = signInt((msbLsb(payload[11], payload[12])), 16)/100;
		var probe_temp = signInt((msbLsb(payload[13], payload[14])), 16)/100;

		switch(odr){
			case 6:
				odr = 50;
				break;
			case 7:
				odr = 100;
				break;
			case 8:
				odr = 200;
				break;
			case 9:
				odr = 400;
				break;
			case 10:
				odr = 800;
				break;
			case 11:
				odr = 1600;
				break;
			default:
				odr = 0;
		}
		globalDevices[deviceAddr] = {
			// stream_size: expected_packets,
			data: {},
			odr: odr,
			// mo: payload[8],
			// en_axis: en_axis,
			hour: hour,
			minute: minute,
			device_temp: device_temp,
			probe_temp: probe_temp
		}
		globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);

		return;
	}
	build_101_data(payload, deviceAddr, hour, minute, sdata_start, current_packet, firmware){
		if(current_packet != 1){
			console.log('bad packet cleanup');
			return;
		}
		var mode = payload[8];
		var odr = payload[9];
		var en_axis = payload[10] & 7;
		var fsr = payload[10] >> 5;

		var device_temp = signInt((msbLsb(payload[13], payload[14])), 16)/100;
		switch(odr){
			case 0:
				odr = 4000;
				break;
			case 1:
				odr = 2000;
				break;
			case 2:
				odr = 1000;
				break;
			case 3:
				odr = 500;
				break;
			case 4:
				odr = 250;
				break;
			case 5:
				odr = 125;
				break;
			case 6:
				odr = 62.5;
				break;
			case 7:
				odr = 31.25;
				break;
			case 8:
				odr = 15.625;
				break;
			case 9:
				odr = 7.813;
				break;
			case 10:
				odr = 3.906;
				break;
			default:
				odr = 0;
		}
		globalDevices[deviceAddr] = {
			// stream_size: expected_packets,
			data: {},
			odr: odr,
			mo: mode,
			fsr: fsr,
			en_axis: en_axis,
			hour: hour,
			minute: minute,
			device_temp: device_temp,
		}
		if(firmware > 0){
			var probe_temp = signInt((msbLsb(payload[15], payload[16])), 16)/100;
			globalDevices[deviceAddr].probe_temp = probe_temp;
		}
		globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);

		return;
	}
	control_send(sensor_mac, data, opts, ctrl_timeout = 1000){
		var that = this;
		return new Promise((fulfill, reject) => {
			that.queue.add(() => {
				return new Promise((f, r) => {
					var failed = false;
					var retries = 0;
					var tO;
					function fail(packet){
						failed = true;
						clearTimeout(tO);
						that._emitter.removeListener('receive_packet-'+sensor_mac, pass);
						that._emitter.removeListener('transmit_status-'+sensor_mac, pass);
						reject({
							err: packet,
							sent: [sensor_mac, data]
						});
						r();
					}
					function pass(packet){
						if(failed) return;
						clearTimeout(tO);
						fulfill(packet);
						f();
					};

					function send(){
						that.send.transmit_request(mac2bytes(sensor_mac), data, opts).then(function(frame){
							if(frame.delivery_status == 'Success'){
								pass(frame);
							}else{
								tO = setTimeout(() => {
									if(retries < 1){
										retries++;
										send();
									}else{
										fail('Control response timeout');
									}
								}, ctrl_timeout);
							}
						}).catch(fail);
					}
					send();
				});
			});
		});
	}
	remote_at_send(sensor_mac, parameter, value, opts, ctrl_timeout = 1000){
		var that = this;
		return new Promise((fulfill, reject) => {
			that.queue.add(() => {
				return new Promise((f, r) => {
					var failed = false;
					var retries = 0;
					var tO;
					function fail(packet){
						failed = true;
						clearTimeout(tO);
						that._emitter.removeListener('receive_packet-'+sensor_mac, pass);
						that._emitter.removeListener('transmit_status-'+sensor_mac, pass);
						reject({
							err: packet,
							sent: [sensor_mac, parameter, value]
						});
						r();
					}
					function pass(packet){
						if(failed) return;
						clearTimeout(tO);
						fulfill(packet);
						f();
					};

					function send(){
						that.send.remote_at_command(mac2bytes(sensor_mac), parameter, value, true).then(function(frame){
							if(frame.status == 'OK'){
								pass(frame);
							}else{
								tO = setTimeout(() => {
									if(retries < 0){
										retries++;
										send();
									}else{
										fail('Remote AT response timeout');
									}
								}, ctrl_timeout);
							}
						}).catch(fail);
					}
					send();
				});
			});
		});
	};
	local_at_send(parameter, value, ctrl_timeout = 1000){
		var that = this;
		return new Promise((fulfill, reject) => {
			that.queue.add(() => {
				return new Promise((f, r) => {
					var failed = false;
					var retries = 0;
					var tO;
					function fail(packet){
						failed = true;
						clearTimeout(tO);
						// that._emitter.removeListener('receive_packet-'+sensor_mac, pass);
						// that._emitter.removeListener('transmit_status-'+sensor_mac, pass);
						reject({
							err: packet,
							sent: ['Local Module', parameter, value]
						});
						r();
					}
					function pass(packet){
						if(failed) return;
						clearTimeout(tO);
						fulfill(packet);
						f();
					};

					function send(){
						that.send.at_command(parameter, value).then(function(frame){
							if(frame.status == 'OK'){
								pass(frame);
							}else{
								tO = setTimeout(() => {
									if(retries < 0){
										retries++;
										send();
									}else{
										fail('AT response timeout');
									}
								}, ctrl_timeout);
							}
						}).catch(fail);
					};
					send();
				});
			});
		});
	};
	route_discover(sensor_mac, opts){
		var data = [85];
		var that = this;
		return new Promise((fulfill, reject) => {
			that.queue.add(() => {
				return new Promise((f, r) => {
					var failed = false;
					var retries = 0;
					var tO;
					function fail(packet){
						failed = true;
						clearTimeout(tO);
						that._emitter.removeListener('receive_packet-'+sensor_mac, pass);
						that._emitter.removeListener('transmit_status-'+sensor_mac, pass);
						reject({
							err: packet,
							sent: [sensor_mac, data]
						});
						r();
					}
					function pass(packet){
						if(failed) return;
						clearTimeout(tO);
						fulfill(packet);
						f();
					};

					function send(){
						that.send.transmit_request(mac2bytes(sensor_mac), data, opts).then(function(frame){
							if(frame.delivery_status == 'Success'){
								pass(frame);
							}else{
								tO = setTimeout(() => {
									if(retries < 1){
										retries++;
										send();
									}else{
										fail('Control response timeout');
									}
								}, 1000);
							}
						}).catch(fail);
					}
					that.query_pool[sensor_mac] = 0;
					if(that.mesh_map.hasOwnProperty(sensor_mac)){
						delete that.mesh_map[sensor_mac];
					}
					that.mesh_map[sensor_mac] = [];
					if(that.link_quality.hasOwnProperty(sensor_mac)){
						delete that.link_quality[sensor_mac];
					}
					that.link_quality = {};
					send();
				});
			});
		});
	}
	link_test(source_mac,destination_mac,opts){
		var that = this;
		return new Promise((fulfill, reject) => {
			that.queue.add(() => {
				return new Promise((f, r) => {
					var failed = false;
					var retries = 0;
					var tO;
					function fail(packet){
						failed = true;
						clearTimeout(tO);
						that._emitter.removeListener('receive_packet-'+source_mac, pass);
						that._emitter.removeListener('transmit_status-'+source_mac, pass);
						reject({
							err: packet,
							sent: [source_mac]
						});
						r();
					}
					function pass(packet){
						if(failed) return;
						clearTimeout(tO);
						fulfill(packet);
						f();
					};

					function send(){
						var cluster = [0,20];
						var profile = [193, 5];
						var data = [];
						data.push(...mac2bytes(destination_mac));
						var payload_size = [0,200];
						var iterations = [0,200];
						data.push(...payload_size);
						data.push(...iterations);

						var hexArray = [];
						data.forEach((b) => hexArray.push(toHex(b)));

						var opts = {"method":3};
						that.send.explicit_addressing_command(mac2bytes(source_mac), 230, 230, cluster, profile, data, opts).then(function(frame){
							if(frame.delivery_status == 'Success'){
								pass(frame);
							}else{
								tO = setTimeout(() => {
									if(retries < 1){
										retries++;
										send();
									}else{
										fail('Control response timeout');
									}
								}, 1000);
							}
						}).catch(fail);
					}
					send();
				});
			});
		});
	}
	on(e,cb){this._emitter.on(e,cb);}
};

function sensor_types(parent){
	var types = {
		'2': {
			name: '2 Channel Push Notification',
			parse: (d, payload) => {
				let firmware = payload[1];
				if(firmware > 5){
					let report_type_byte = (payload[7] >> 5) & 1;
					let report_type = "";
					switch(report_type_byte){
						case 0: report_type = 'Regular'; break;
						case 1: report_type = 'Interrupt'; break;
					}
					return{
						report_type: report_type,
						input_1: d[0],
						input_2: d[1]
					}
				}
				return {
					input_1: d[0],
					input_2: d[1]
				};
			}
		},
		'3': {
			name: 'ADC',
			parse: (d) => {
				return {
					input_1: msbLsb(d[0], d[1]),
					input_2: msbLsb(d[2], d[3])
				};
			}
		},
		'5': {
			name: 'Gyro/Magneto/Temperature',
			parse: (d) => {
				return {
					accel_x: signInt(d.slice(0, 3).reduce(msbLsb), 24)/100,
					accel_y: signInt(d.slice(3, 6).reduce(msbLsb), 24)/100,
					accel_z: signInt(d.slice(6, 9).reduce(msbLsb), 24)/100,
					magneto_x: signInt(d.slice(9, 12).reduce(msbLsb), 24)/100,
					magneto_y: signInt(d.slice(12, 15).reduce(msbLsb), 24)/100,
					magneto_z: signInt(d.slice(15, 18).reduce(msbLsb), 24)/100,
					gyro_x: signInt(d.slice(18, 21).reduce(msbLsb), 24),
					gyro_y: signInt(d.slice(21, 24).reduce(msbLsb), 24),
					gyro_z: signInt(d.slice(24, 27).reduce(msbLsb), 24),
					temperature: signInt(msbLsb(d[27], d[28]), 16)
				};
			}
		},
		'6': {
			name: 'Temperature/Barometeric Pressure',
			parse: (d) => {
				return {
					temperature: signInt(msbLsb(d[0], d[1]), 16),
					absolute_pressure: msbLsb(d[2], d[3])/1000,
					relative_pressure: signInt(msbLsb(d[4], d[5]), 16)/1000,
					altitude_change: signInt(msbLsb(d[6], d[7]), 16)/100
				};
			}
		},
		'7': {
			name: 'Impact Detection',
			parse: (d) => {
				return {
					acc_x1: signInt(d.slice(0, 2).reduce(msbLsb), 16),
					acc_x2: signInt(d.slice(2, 4).reduce(msbLsb), 16),
					acc_x: signInt(d.slice(4, 6).reduce(msbLsb), 16),
					acc_y1: signInt(d.slice(6, 8).reduce(msbLsb), 16),
					acc_y2: signInt(d.slice(8, 10).reduce(msbLsb), 16),
					acc_y: signInt(d.slice(10, 12).reduce(msbLsb), 16),
					acc_z1: signInt(d.slice(12, 14).reduce(msbLsb), 16),
					acc_z2: signInt(d.slice(14, 16).reduce(msbLsb), 16),
					acc_z: signInt(d.slice(16, 18).reduce(msbLsb), 16),
					temp_change: signInt(d.slice(18, 20).reduce(msbLsb), 16)
				};
			}
		},
		'8': {
			name: 'Vibration',
			parse: (d) => {
				return {
					rms_x: signInt(d.slice(0, 3).reduce(msbLsb), 24)/100,
					rms_y: signInt(d.slice(3, 6).reduce(msbLsb), 24)/100,
					rms_z: signInt(d.slice(6, 9).reduce(msbLsb), 24)/100,
					max_x: signInt(d.slice(9, 12).reduce(msbLsb), 24)/100,
					max_y: signInt(d.slice(12, 15).reduce(msbLsb), 24)/100,
					max_z: signInt(d.slice(15, 18).reduce(msbLsb), 24)/100,
					min_x: signInt(d.slice(18, 21).reduce(msbLsb), 24)/100,
					min_y: signInt(d.slice(21, 24).reduce(msbLsb), 24)/100,
					min_z: signInt(d.slice(24, 27).reduce(msbLsb), 24)/100,
					temperature: signInt(msbLsb(d[27], d[28]), 16)
				};
			}
		},
		'9': {
			name: 'Proximity',
			parse: (d) => {
				return {
					proximity: msbLsb(d[0], d[1]),
					lux: msbLsb(d[2], d[3]) * .25
				};
			}
		},
		'10': {
			name: 'Light',
			parse: (d) => {
				return {
					lux: d.slice(0, 3).reduce(msbLsb)
				};
			}
		},
		'13': {
			name: 'Current Monitor',
			parse: (d) => {
				return {
					amps: d.slice(0, 3).reduce(msbLsb)/1000
				};
			}
		},
		'14': {
			name: '10-Bit 1-Channel 4-20mA',
			parse: (d) => {
				var adc = d.slice(0, 2).reduce(msbLsb);
				return {
					adc: adc,
					mA: adc * 20 / 998
				};
			}
		},
		'16': {
			name: 'Soil Moisture Sensor',
			parse: (d, payload) => {
				if(payload[1] > 5) // firmware v6+
				{
					var adc1 = d.slice(0, 2).reduce(msbLsb);
					var adc2 = d.slice(2, 4).reduce(msbLsb);
					var vwc1 = d.slice(4, 6).reduce(msbLsb);
					var vwc2 = d.slice(6, 8).reduce(msbLsb);
					return {
						adc1: adc1,
						adc2: adc2,
						vwc1: vwc1,
						vwc2: vwc2,
						voltage1: adc1 * 0.00322265625,
						voltage2: adc2 * 0.00322265625,
						percentage1: adc1 > 870 ? 100 : Math.round(adc1 / 870 * 100),
						percentage2: adc2 > 870 ? 100 : Math.round(adc2 / 870 * 100)
					};
				} else{
					var adc1 = d.slice(0, 2).reduce(msbLsb);
					var adc2 = d.slice(2, 4).reduce(msbLsb);
					return {
						adc1: adc1,
						adc2: adc2,
						voltage1: adc1 * 0.00322265625,
						voltage2: adc2 * 0.00322265625,
						percentage: adc1 > 870 ? 100 : Math.round(adc1 / 870 * 100)
					};
				}
			}
		},
		'17': {
			name: '24-Bit AC Voltage Monitor',
			parse: (d) => {
				return {
					voltage: d.slice(0, 3).reduce(msbLsb) / 1000
				};
			}
		},
		'18': {
			name: 'Pulse/Frequency Meter',
			parse: (d) => {
				return {
					frequency: d.slice(0, 3).reduce(msbLsb) / 1000,
					duty_cycle: d.slice(3, 5).reduce(msbLsb) / 100
				};
			}
		},
		'19': {
			name: '2-Channel Current Monitor',
			parse: (d) => {
				return {
					channel_1: d.slice(0, 3).reduce(msbLsb),
					channel_2: d.slice(4, 7).reduce(msbLsb)
				};
			}
		},
		'22': {
			name: 'Voltage Detection Input',
			parse: (d) => {
				return {
					input: d[0]
				};
			}
		},
		'25': {
			name: 'Asset Monitor',
			parse: (d) => {
				return {
					acc_x: signInt(d.slice(0, 2).reduce(msbLsb), 16),
					acc_y: signInt(d.slice(2, 4).reduce(msbLsb), 16),
					acc_z: signInt(d.slice(4, 6).reduce(msbLsb), 16),
					temp_change: signInt(d.slice(6, 8).reduce(msbLsb), 16),
				};
			}
		},
		'26': {
			name: 'Pressure & Temperature Sensor (PSI)',
			parse: (d) => {
				return {
					pressure: signInt(d.slice(0, 4).reduce(msbLsb), 32) / 100,
					temperature: signInt(d.slice(4, 6).reduce(msbLsb), 16) / 100
				};
			}
		},
		'27': {
			name: 'Environmental',
			parse: (d) => {
				return {
					temperature: signInt(d.slice(0, 2).reduce(msbLsb), 16) / 100,
					pressure: d.slice(2, 6).reduce(msbLsb) / 100,
					humidity: d.slice(6, 10).reduce(msbLsb) / 1000,
					gas_resistance: d.slice(10, 14).reduce(msbLsb),
					iaq: d.slice(14, 16).reduce(msbLsb)

				};
			}
		},
		'28': {
			'name': '3-Phase Current Sensor',
			parse: (d) => {
				return {
					channel_1: d.slice(0, 3).reduce(msbLsb),
					channel_2: d.slice(4, 7).reduce(msbLsb),
					channel_3: d.slice(8, 11).reduce(msbLsb)
				};
			}
		},
		'29': {
			'name': 'Linear Displacement Sensor',
			parse: (payload, parsed, mac) => {
				if(payload[7] >> 1 != 0){
					console.log('Error found');
					parsed.data = {error: 'Error found, invalid data'};
					return parsed;
				}
				if(parsed.firmware > 4){
					return {
						position: (payload.slice(8, 10).reduce(msbLsb))/100
					};
				}else{
					var adc = payload.slice(8, 10).reduce(msbLsb);
					return {
						adc: adc,
						position: adc/1023*100,
					};
				}
			},
			'parse_fly': (frame) => {
				let firmware = frame[2];
				if(firmware > 4){
					return {
						'firmware': frame[2],
						'sensor_length': (frame.slice(12, 14).reduce(msbLsb))+"mm",
						'hardware_id': frame.slice(14, 17),
						'report_rate': frame.slice(17, 21).reduce(msbLsb)+"sec",
						'tx_life_counter': frame.slice(21, 25).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'sensor_length': frame.slice(12, 14),
							'hardware_id': frame.slice(14, 17),
							'report_rate': frame.slice(17, 21),
							'tx_life_counter': frame.slice(21, 25)
						}
					}
				}
			}
		},
		'30': {
			'name': 'Structural Monitoring Sensor',
			parse: (d) => {
				var adc = d.slice(0, 2).reduce(msbLsb);
				return {
					adc: adc,
					position: adc/1023*100,
				};
			}
		},
		'31': {
			name: 'Temperature/Humidity VOC Sensor',
			parse: (d) => {
				return {
					humidity: 		d.slice(0, 2).reduce(msbLsb) / 100,
					temperature: 	signInt(d.slice(2, 4).reduce(msbLsb), 16) / 100,
					voc: 			d.slice(4, 6).reduce(msbLsb)
				};
			}
		},
		'32': {
			'name': 'Particulate Matter Sensor',
			parse: (d) => {
				return {
					mass_concentration_1_0:    d.slice(0, 4).reduce(msbLsb)/100,
					mass_concentration_2_5:    d.slice(4, 8).reduce(msbLsb)/100,
					mass_concentration_4_0:    d.slice(8, 12).reduce(msbLsb)/100,
					mass_concentration_10_0:   d.slice(12, 16).reduce(msbLsb)/100,
					number_concentration_0_5:  d.slice(16, 20).reduce(msbLsb)/100,
					number_concentration_1_0:  d.slice(20, 24).reduce(msbLsb)/100,
					number_concentration_2_5:  d.slice(24, 28).reduce(msbLsb)/100,
					number_concentration_4_0:  d.slice(28, 32).reduce(msbLsb)/100,
					number_concentration_10_0: d.slice(32, 36).reduce(msbLsb)/100,
					typical_size:              d.slice(36, 40).reduce(msbLsb)/100,
					Humidity:              d.slice(40, 42).reduce(msbLsb)/100,
					Temperature:              d.slice(42, 44).reduce(msbLsb)/100

				};
			}
		},
		'34': {
			name: 'Tank Level Sensor',
			parse: (d) => {
				return {
					level: msbLsb(d[0], d[1])
				};
			}
		},
		'35': {
			name: 'One Channel Counter',
			parse: (d, payload) => {
				if(payload[1] > 9){ // Firmware v10 and above
					let report_type = "Regular";
					switch(d[9]){
						case 0:
							report_type = "Regular";
							break;
						case 1:
							report_type = "Shift end";
							break;
						case 2:
							report_type = "Interrupt";
							break;
						case 3:
							report_type = "Threshold";
							break;
					}
					return {
						counter: d.slice(0, 4).reduce(msbLsb),
						uptime: d.slice(4, 8).reduce(msbLsb),
						input_status: d[8] & 1 ? 1 : 0,
						report_type: report_type,
						rtc: [
							String(d[10]).padStart(2, '0'),
							String(d[11]).padStart(2, '0'),
							String(d[12]).padStart(2, '0')
						].join(':')
					};
				}else {
					return {
						counts: d.slice(0, 4).reduce(msbLsb)
					};
				}
			}
		},
		'36': {
			name: 'Two Channel Counter',
			parse: (d) => {
				return {
					counts_1: msbLsb(d[0], d[1]),
					counts_2: msbLsb(d[2], d[3])
				};
			}
		},
		'37': {
			name: '7 Channel Push Notification',
			parse: (d) => {
				return {
					input_1: d[0] & 1 ? 1 : 0,
					input_2: d[0] & 2 ? 1 : 0,
					input_3: d[0] & 4 ? 1 : 0,
					input_4: d[0] & 8 ? 1 : 0,
					input_5: d[0] & 16 ? 1 : 0,
					input_6: d[0] & 32 ? 1 : 0,
					input_7: d[0] & 64 ? 1 : 0,
					adc_1: msbLsb(d[1], d[2]),
					adc_2: msbLsb(d[3], d[4]),
				};
			}
		},
		'40': {
			name: 'Vibration w/Time Domain (partial support)',
			parse: (d, full) => {
				var status = {
					0: 'Valid',
					63: 'Invalid Argument',
					62: 'Internal Sensor Communication Failure',
					61: 'Invalid Sensor Discovery',
					60: 'Invalid Length',
					59: 'ASIC Test Failure',
					58: 'Device Initialization Failure',
					57: 'Soft Reset Failure'
				};
				return {
					status: status[full[7] >> 2],
					reserve: full[7],
					data_type: ['unknown', 'Acceleration', 'Velocity', 'Time Domain'][full[7] & 3],
					rms_x: signInt(d.slice(0, 3).reduce(msbLsb), 24)/100,
					rms_y: signInt(d.slice(3, 6).reduce(msbLsb), 24)/100,
					rms_z: signInt(d.slice(6, 9).reduce(msbLsb), 24)/100,
					max_x: signInt(d.slice(9, 12).reduce(msbLsb), 24)/100,
					max_y: signInt(d.slice(12, 15).reduce(msbLsb), 24)/100,
					max_z: signInt(d.slice(15, 18).reduce(msbLsb), 24)/100,
					min_x: signInt(d.slice(18, 21).reduce(msbLsb), 24)/100,
					min_y: signInt(d.slice(21, 24).reduce(msbLsb), 24)/100,
					min_z: signInt(d.slice(24, 27).reduce(msbLsb), 24)/100,
					temperature: signInt(msbLsb(d[27], d[28]), 16)
				};
			}
		},
		'41': {
			name: 'RPM',
			parse: (d) => {
				return {
					proximity: msbLsb(d[0], d[1]),
					rpm: msbLsb(d[2], d[3]) * .25
				};
			}
		},
		'42': {
			name: '0-24VDC Voltage Monitor',
			parse: (d) => {
				var adc = d.slice(0, 2).reduce(msbLsb);
				return {
					adc: adc,
					voltage: adc * 0.00122265625
				};
			}
		},
		'44': {
			name: 'Wireless CO2 Gas Sensor',
			parse: (d) => {
				return {
					CO2:    d.slice(0, 4).reduce(msbLsb)/100,
					humidity: msbLsb(d[4], d[5])/100,
					temperature: signInt((msbLsb(d[6], d[7])), 16)/100
				};
			}
		},
		'46': {
			name: 'Motion Detection Sensor',
			parse: (d) => {
				return {
					input_1: d[0]
				};
			}
		},
		'47': {
			name: 'Wireless Tilt Sensor',
			parse: (d) => {
				return {
					Roll: signInt(d.slice(0, 2).reduce(msbLsb), 16) / 100,
					Pitch: signInt(d.slice(2, 4).reduce(msbLsb), 16) / 100
				};
			}
		},
		'49': {
			name: '6-Channel Thermocouple',
			parse: (d) => {
				return {
					channel_1: signInt(d.slice(0, 4).reduce(msbLsb), 32) / 100,
					channel_2: signInt(d.slice(4, 8).reduce(msbLsb), 32) / 100,
					channel_3: signInt(d.slice(8, 12).reduce(msbLsb), 32) / 100,
					channel_4: signInt(d.slice(12, 16).reduce(msbLsb), 32) / 100,
					channel_5: signInt(d.slice(16, 20).reduce(msbLsb), 32) / 100,
					channel_6: signInt(d.slice(20, 24).reduce(msbLsb), 32) / 100
				};
			}
		},
		'50': {
			name: 'Predictive Maintenance Sensor',
			parse: (d) => {
				return {
					rms_x: signInt(d.slice(0, 3).reduce(msbLsb), 24)/100,
					rms_y: signInt(d.slice(3, 6).reduce(msbLsb), 24)/100,
					rms_z: signInt(d.slice(6, 9).reduce(msbLsb), 24)/100,
					max_x: signInt(d.slice(9, 12).reduce(msbLsb), 24)/100,
					max_y: signInt(d.slice(12, 15).reduce(msbLsb), 24)/100,
					max_z: signInt(d.slice(15, 18).reduce(msbLsb), 24)/100,
					min_x: signInt(d.slice(18, 21).reduce(msbLsb), 24)/100,
					min_y: signInt(d.slice(21, 24).reduce(msbLsb), 24)/100,
					min_z: signInt(d.slice(24, 27).reduce(msbLsb), 24)/100,
					vibration_temperature: signInt(msbLsb(d[27], d[28]), 16),
					thermocouple_temperature: signInt(d.slice(29, 33).reduce(msbLsb), 32) / 100,
					current: signInt(d.slice(33, 36).reduce(msbLsb), 24) / 1000
				};
			}
		},
		'53': {
			'name': 'Air Quality CO2 and Particulate Matter Sensor',
			parse: (d) => {
				return {
					mass_concentration_1_0:    d.slice(0, 4).reduce(msbLsb)/100,
					mass_concentration_2_5:    d.slice(4, 8).reduce(msbLsb)/100,
					mass_concentration_4_0:    d.slice(8, 12).reduce(msbLsb)/100,
					mass_concentration_10_0:   d.slice(12, 16).reduce(msbLsb)/100,
					number_concentration_0_5:  d.slice(16, 20).reduce(msbLsb)/100,
					number_concentration_1_0:  d.slice(20, 24).reduce(msbLsb)/100,
					number_concentration_2_5:  d.slice(24, 28).reduce(msbLsb)/100,
					number_concentration_4_0:  d.slice(28, 32).reduce(msbLsb)/100,
					number_concentration_10_0: d.slice(32, 36).reduce(msbLsb)/100,
					typical_size:              d.slice(36, 40).reduce(msbLsb)/100,
					Humidity:              d.slice(40, 42).reduce(msbLsb)/100,
					Temperature:              d.slice(42, 44).reduce(msbLsb)/100,
					CO2:              d.slice(44, 48).reduce(msbLsb)/100

				};
			}
		},
		'58': {
			name: 'Tank Level v3',
			parse: (d) => {
				return {
					filtered_range: d.slice(0, 2).reduce(msbLsb),
					long_range_algorithm: d.slice(2, 4).reduce(msbLsb),
					second_chance:  d.slice(4, 6).reduce(msbLsb),
					second_reading: d.slice(6, 8).reduce(msbLsb),
					compare_reading:  d.slice(8, 10).reduce(msbLsb),
					short_range_algorithm: d.slice(10, 12).reduce(msbLsb),
					trusted_reading:  d.slice(12, 14).reduce(msbLsb),
					final_filter:  d.slice(14, 16).reduce(msbLsb),
					raw_final:  d.slice(16, 18).reduce(msbLsb),
					final_index:  d.slice(18, 20).reduce(msbLsb),
				};
			}
		},
		'61': {
			name: 'pH and Temperature Sensor',
			parse: (d) => {
				return {
					pH: signInt(d.slice(0, 2).reduce(msbLsb), 16) / 100,
					Temp: signInt(d.slice(2, 4).reduce(msbLsb),16) / 100
				};
			}
		},
		'62': {
			name: 'ORP and Temperature Sensor',
			parse: (d) => {
				return {
					ORP: signInt(d.slice(0, 2).reduce(msbLsb), 16),
					Temp: signInt(d.slice(2, 4).reduce(msbLsb),16) / 100
				};
			}
		},
		'63': {
			name: 'ORP, pH and Temperature Sensor',
			parse: (d) => {
				return {
					ORP: signInt(d.slice(0, 2).reduce(msbLsb), 16),
					Temp: signInt(d.slice(2, 4).reduce(msbLsb),16) / 100,
					pH: signInt(d.slice(4, 6).reduce(msbLsb), 16) / 100,
					Temp: signInt(d.slice(6, 8).reduce(msbLsb),16) / 100
				};
			}
		},
		'64': {
			name: 'EC Salinity TDS and Temperature Sensor',
			parse: (d) => {
				return {
					EC: signInt(d.slice(0, 4).reduce(msbLsb), 32) / 100,
					TDS: signInt(d.slice(4, 8).reduce(msbLsb), 32) / 100,
					Salinity: signInt(d.slice(8, 12).reduce(msbLsb), 32) / 100,
					Temp: signInt(d.slice(12, 14).reduce(msbLsb),16) / 100
				};
			}
		},

		'65': {
			name: 'Dissolved Oxygen and Temperature Sensor',
			parse: (d) => {
				return {
					DO: signInt(d.slice(0, 4).reduce(msbLsb), 32) / 100,
					DO_Saturation: signInt(d.slice(4, 8).reduce(msbLsb), 32) / 100,
					Temp: signInt(d.slice(8, 10).reduce(msbLsb),16) / 100
				};
			}
		},

		'66': {
			name: 'EC and Dissolved Oxygen and Temperature Sensor',
			parse: (d) => {
				return {
					EC: signInt(d.slice(0, 4).reduce(msbLsb), 32) / 100,
					TDS: signInt(d.slice(4, 8).reduce(msbLsb), 32) / 100,
					Salinity: signInt(d.slice(8, 12).reduce(msbLsb), 32) / 100,
					Temp: signInt(d.slice(12, 14).reduce(msbLsb),16) / 100,
					DO: signInt(d.slice(14, 18).reduce(msbLsb), 32) / 100,
					DO_Saturation: signInt(d.slice(18, 22).reduce(msbLsb), 32) / 100,
					Temp_DO: signInt(d.slice(22, 24).reduce(msbLsb),16) / 100
				};
			}
		},

		'67': {
			name: 'PAR Sensor',
			parse: (d) => {
				return {
					PAR: signInt(d.slice(0, 4).reduce(msbLsb), 32) / 100
				};
			}
		},
		'69': {
			name: 'Soil Moisture Temperature EC Sensor',
			parse: (d) => {
				return {
					Moisture: signInt(d.slice(0, 4).reduce(msbLsb), 32) / 100,
					Temperature: signInt(d.slice(4, 8).reduce(msbLsb), 32) / 100,
					EC: signInt(d.slice(8, 12).reduce(msbLsb), 32) / 100
				};
			}
		},

		'71': {
			name: '3 Channel Soil Moisture Temperature and EC Sensor',
			parse: (d) => {
				return {
					Moisture1: signInt(d.slice(0, 4).reduce(msbLsb), 32) / 100,
					Temperature1: signInt(d.slice(4, 8).reduce(msbLsb), 32) / 100,
					EC1: signInt(d.slice(8, 12).reduce(msbLsb), 32) / 100,
					Salinity1: signInt(d.slice(12, 16).reduce(msbLsb), 32) / 100,

					Moisture2: signInt(d.slice(16, 20).reduce(msbLsb), 32) / 100,
					Temperature2: signInt(d.slice(20, 24).reduce(msbLsb), 32) / 100,
					EC2: signInt(d.slice(24, 28).reduce(msbLsb), 32) / 100,
					Salinity2: signInt(d.slice(28, 32).reduce(msbLsb), 32) / 100,

					Moisture3: signInt(d.slice(32, 36).reduce(msbLsb), 32) / 100,
					Temperature3: signInt(d.slice(36, 40).reduce(msbLsb), 32) / 100,
					EC3: signInt(d.slice(40, 44).reduce(msbLsb), 32) / 100,
					Salinity3: signInt(d.slice(44, 48).reduce(msbLsb), 32) / 100
				};
			}
		},

		'72': {
			name: 'SDI-12 Wireelss',
			parse: (d) => {
				return {
					Temperature: signInt(d.slice(0, 2).reduce(msbLsb), 16)/100,
					Soil_Moisture: signInt(d.slice(2, 4).reduce(msbLsb), 16)/100,
					Bulk_EC: signInt(d.slice(4, 6).reduce(msbLsb), 16),
					Pore_EC: signInt(d.slice(6, 8).reduce(msbLsb), 16),
					Permittivity: signInt(d.slice(8, 10).reduce(msbLsb), 16)/100,
				};
			}
		},
		'74': {
			name: 'Wireless Temp Humidity Pressure Air quality Sensor V2',
			parse: (d, parsed) => {
				let reserved = parsed[7];
					let status = '';
					switch (reserved){
						case 0:
							status = 'success';
						break;
						case 1:
							status = 'connection_failed';
						break;
						case 2:
							status = 'no_new_data';
						break;
						case 3:
							status = 'sensor_not_found';
						break;
						case 4:
							status = 'sensor_comm_failed';
						break;
						case 5:
							status = 'sensor_generic_error';
						break;
					}
				if(parsed[1] > 1){ // Firmware version
					return {
						status: status,
						temperature: signInt(d.slice(0, 2).reduce(msbLsb), 16) / 100,
						pressure: d.slice(2, 6).reduce(msbLsb) / 100,
						humidity: d.slice(6, 10).reduce(msbLsb) / 1000,
						resistance: d.slice(10, 14).reduce(msbLsb),
						iaq: d.slice(14, 16).reduce(msbLsb),
						co2_eqv: d.slice(16, 18).reduce(msbLsb),
						breath_voc: d.slice(18, 22).reduce(msbLsb) / 100,
						static_iaq:  d.slice(22, 26).reduce(msbLsb) / 100,
						iaq_accuracy: d[26]
					};
				}
				else{
					return {
						status: status,
						temperature: signInt(d.slice(0, 2).reduce(msbLsb), 16) / 100,
						pressure: d.slice(2, 6).reduce(msbLsb) / 100,
						humidity: d.slice(6, 10).reduce(msbLsb) / 1000,
						resistance: d.slice(10, 14).reduce(msbLsb),
						iaq: d.slice(14, 16).reduce(msbLsb),
						co2_eqv: d.slice(16, 18).reduce(msbLsb),
						breath_voc: d.slice(18, 22).reduce(msbLsb)/ 100,
						static_iaq:  (d.slice(22, 26).reverse()).reduce(msbLsb)/ 100,
						// TODO Check if the iaq_accuracy should still be d[27]
						iaq_accuracy: d[26]
					};
				}
			},
			'parse_fly': (frame) => {
				if(frame[2] > 2){
					return {
						'firmware': frame[2],
						'calibration_days': frame[12] + ' days',
						'sensor_update_rate':frame.slice(13, 15).reduce(msbLsb) + ' sec',
						'heater_duration': frame.slice(15, 17).reduce(msbLsb) + ' msec',
						'heater_temperature': frame.slice(17, 19).reduce(msbLsb) + ' C',
						'hardware_id': frame.slice(19, 22),
						'report_rate': frame.slice(22, 26).reduce(msbLsb) + ' sec',
						'tx_counter': frame.slice(26, 30).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'calibration_days': frame[12],
							'sensor_update_rate':frame.slice(13, 15),
							'heater_duration': frame.slice(15, 17),
							'heater_temperature': frame.slice(17, 19),
							'hardware_id': frame.slice(19, 22),
							'report_rate': frame.slice(22, 26).reduce(msbLsb),
							'tx_counter': frame.slice(26, 30).reduce(msbLsb),

						}
					};
				}
				else{
					return {
						'firmware': frame[2],
						'calibration_days': frame[12] + ' days',
						'sensor_update_rate':frame.slice(13, 15).reduce(msbLsb) + ' sec',
						'hardware_id': frame.slice(15, 18),
						'report_rate': frame.slice(18, 22).reduce(msbLsb) + ' sec',
						'tx_counter': frame.slice(22, 26).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'calibration_days': frame[12],
							'sensor_update_rate':frame.slice(13, 15),
							'hardware_id': frame.slice(15, 18),
							'report_rate': frame.slice(18, 22),
							'tx_counter': frame.slice(22, 26),
						}
					};
				}
			}
		},
		'76': {
			name: 'Wireless CO Sensor',
			parse: (d) => {
				return {
					humidity: msbLsb(d[0], d[1])/100,
					temperature: signInt((msbLsb(d[2], d[3])), 16)/100,
					co_ppm: msbLsb(d[4], d[5])
				};
			},
			'parse_fly': (frame) => {
				let firmware = frame[2];
				if(firmware > 13){ // firmware 14 and above
					let frame_data = {};
					let auto_check_interval = frame.slice(20, 22).reduce(msbLsb);
					if(!auto_check_interval){
						frame_data.auto_check_interval = 'Disabled';
					}else{
						frame_data.auto_check_interval = auto_check_interval + " sec";
					}
					frame_data.always_on = frame[24]?"Enabled":"Disabled";
					switch(frame[16]){
						case 0:
							frame_data.fsr = "+-6.114 V";
						break;
						case 1:
							frame_data.fsr = "+-4.096 V";
						break;
						case 2:
							frame_data.fsr = "+-2.048 V";
						break;
						case 3:
							frame_data.fsr = "+-1.024 V";
						break;
						case 4:
							frame_data.fsr = "+-0.512 V";
						break;
						case 5:
							frame_data.fsr = "+-0.256 V";
						break;
					}
					return {
						'firmware': frame[2],
						'fsr': frame_data.fsr,
						'boot_up_time': frame[17] + " sec",
						'adc_pin_reading': frame.slice(18, 20).reduce(msbLsb),
						'auto_check_interval': frame_data.auto_check_interval,
						'auto_check_threshold': frame.slice(22, 24).reduce(msbLsb),
						'always_on': frame_data.always_on,
						'calibration_one': frame.slice(25, 29).reduce(msbLsb),
						'calibration_two':frame.slice(29, 33).reduce(msbLsb),
						'calibration_three':frame.slice(33, 37).reduce(msbLsb),
						'hardware_id': frame.slice(37, 40),
						'report_rate': frame.slice(40, 44).reduce(msbLsb) + " sec",
						'tx_life_counter': frame.slice(44, 48).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'fsr': frame[16],
							'boot_up_time': frame[17],
							'adc_pin_reading': frame.slice(18, 20),
							'auto_check_interval': frame.slice(20, 22),
							'auto_check_percentage': frame.slice(22, 24),
							'always_on': frame[24],
							'calibration_one': frame.slice(25, 29),
							'calibration_two':frame.slice(29, 33),
							'calibration_three':frame.slice(33, 37),
							'hardware_id': frame.slice(37, 40),
							'report_rate': frame.slice(40, 44),
							'tx_life_counter': frame.slice(44, 48)
						}
					}
				}
			}
		},
		'77': {
			name: '3 Channel SDI-12 Wireelss',
			parse: (d) => {
				return {
					Temperature_1: signInt(d.slice(0, 2).reduce(msbLsb), 16)/100,
					Soil_Moisture_1: signInt(d.slice(2, 4).reduce(msbLsb), 16)/100,
					Bulk_EC_1: signInt(d.slice(4, 6).reduce(msbLsb), 16),
					Pore_EC_1: signInt(d.slice(6, 8).reduce(msbLsb), 16),
					Permittivity_1: signInt(d.slice(8, 10).reduce(msbLsb), 16)/100,
					Temperature_2: signInt(d.slice(10, 12).reduce(msbLsb), 16)/100,
					Soil_Moisture_2: signInt(d.slice(12, 14).reduce(msbLsb), 16)/100,
					Bulk_EC_2: signInt(d.slice(14, 16).reduce(msbLsb), 16),
					Pore_EC_2: signInt(d.slice(16, 18).reduce(msbLsb), 16),
					Permittivity_2: signInt(d.slice(18, 20).reduce(msbLsb), 16)/100,
					Temperature_3: signInt(d.slice(20, 22).reduce(msbLsb), 16)/100,
					Soil_Moisture_3: signInt(d.slice(22, 24).reduce(msbLsb), 16)/100,
					Bulk_EC_3: signInt(d.slice(24, 26).reduce(msbLsb), 16),
					Pore_EC_3: signInt(d.slice(26, 28).reduce(msbLsb), 16),
					Permittivity_3: signInt(d.slice(28, 30).reduce(msbLsb), 16)/100
				};
			}
		},
		'78': {
			name: 'Oil Particulate Counter Sensor',
			parse: (d, original_payload) => {
				if(original_payload[1] <= 3){
					return {
						ferro_particles_30_50_um: d.slice(0, 4).reduce(msbLsb)/100,
						ferro_particles_50_100_um: d.slice(4, 8).reduce(msbLsb)/100,
						ferro_particles_100_200_um: d.slice(8, 12).reduce(msbLsb)/100,
						ferro_particles_200_400_um: d.slice(12, 16).reduce(msbLsb)/100,
						ferro_particles_400_800_um: d.slice(16, 20).reduce(msbLsb)/100,
						ferro_particles_gt_800_um: d.slice(20, 24).reduce(msbLsb)/100,
						ferro_particles_total: d.slice(24, 28).reduce(msbLsb)/100,
						non_ferro_particles_120_200_um: d.slice(28, 32).reduce(msbLsb)/100,
						non_ferro_particles_200_400_um: d.slice(32, 36).reduce(msbLsb)/100,
						non_ferro_particles_400_800_um: d.slice(36, 40).reduce(msbLsb)/100
					};
				}else{
					return {
						ferro_particles_30_50_um: d.slice(0, 4).reduce(msbLsb)/100,
						ferro_particles_50_100_um: d.slice(4, 8).reduce(msbLsb)/100,
						ferro_particles_100_200_um: d.slice(8, 12).reduce(msbLsb)/100,
						ferro_particles_200_400_um: d.slice(12, 16).reduce(msbLsb)/100,
						ferro_particles_400_800_um: d.slice(16, 20).reduce(msbLsb)/100,
						ferro_particles_gt_800_um: d.slice(20, 24).reduce(msbLsb)/100,
						ferro_particles_total: d.slice(24, 28).reduce(msbLsb)/100,
						non_ferro_particles_120_200_um: d.slice(28, 32).reduce(msbLsb)/100,
						non_ferro_particles_200_400_um: d.slice(32, 36).reduce(msbLsb)/100,
						non_ferro_particles_400_800_um: d.slice(36, 40).reduce(msbLsb)/100,
						non_ferro_particles_800_1600_um: d.slice(40, 44).reduce(msbLsb)/100,
						non_ferro_particles_gt_1600_um: d.slice(44, 48).reduce(msbLsb)/100,
						non_ferro_particles_total: d.slice(48, 52).reduce(msbLsb)/100,
						ferro_particles_total_24h: d.slice(52, 56).reduce(msbLsb)/100,
						non_ferro_particles_total_24h: d.slice(56, 60).reduce(msbLsb)/100,
						ferro_concentration: d.slice(60, 64).reduce(msbLsb)/100,
						non_ferro_concentration: d.slice(64, 68).reduce(msbLsb)/100,
						flow: d.slice(68, 72).reduce(msbLsb)/100,
					};
				}
			}
		},
		'79': {
			name: 'Oil Analysis Sensor',
			parse: (d) => {
				return {
					dynamic_viscosity: signInt(d.slice(0, 4).reduce(msbLsb), 32) / 100,
					density: signInt(d.slice(4, 8).reduce(msbLsb), 32) / 100,
					dialectric_constant: signInt(d.slice(8, 12).reduce(msbLsb), 32) / 100,
					temperature: signInt(d.slice(12, 16).reduce(msbLsb), 32) / 100,
					saturability: signInt(d.slice(16, 20).reduce(msbLsb), 32) / 100,
					moisture_content: signInt(d.slice(20, 24).reduce(msbLsb), 32) / 100,
					water_content: signInt(d.slice(24, 28).reduce(msbLsb), 32) / 100,
					kinematic_viscosity_40c: signInt(d.slice(28, 32).reduce(msbLsb), 32) / 100,
					kinematic_viscosity_100c: signInt(d.slice(32, 36).reduce(msbLsb), 32) / 100
				};
			}
		},
		'80': {
			name: 'One Channel Vibration Plus',
			parse: (payload, parsed, mac) => {
				if(payload[7] >> 1 != 0){
					console.log('Error found');
					parsed.data = {error: 'Error found, Sensor Probe may be unattached'};
					return parsed;
				}

				if(payload[8] === 1){
					var deviceAddr = mac;
					var expected_packets = payload[15];
					var current_packet = payload[16];
					var sdata_start = 17;

					if(globalDevices.hasOwnProperty(deviceAddr) || expected_packets == 1){
						if(expected_packets != 1){
							// if current packet is equal to last one (duplicated data). This does not apply to the last package
							if (globalDevices[deviceAddr].last_packet_counter == current_packet){
								console.log('Duplicated message')
								return;
							}
							// if current packet is equal to 1 or last packet counter is higher thant current packet
							if(current_packet == 1 || (globalDevices[deviceAddr].last_packet_counter > current_packet)){
								console.log('Recovering bad packet');
								// clear stream object & timeout
								parent.sensor_types[80].utils.clear_globalDevices_stream(deviceAddr);

								// init new stream
								parent.sensor_types[80].utils.init_globalDevices_stream(deviceAddr, payload, expected_packets, parsed);

								globalDevices[deviceAddr].last_packet_counter = current_packet;
								globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
								return;
							}
							else{
								globalDevices[deviceAddr].last_packet_counter = current_packet;
								globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
							}
						}
						else{
							// clear stream object & timeout
							parent.sensor_types[80].utils.clear_globalDevices_stream(deviceAddr);

							// init new stream
							parent.sensor_types[80].utils.init_globalDevices_stream(deviceAddr, payload, expected_packets, parsed);

							globalDevices[deviceAddr].last_packet_counter = current_packet;
							globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						}
					}
					else{
						// clear stream object & timeout
						parent.sensor_types[80].utils.clear_globalDevices_stream(deviceAddr);

						// init new stream
						parent.sensor_types[80].utils.init_globalDevices_stream(deviceAddr, payload, expected_packets, parsed);

						globalDevices[deviceAddr].last_packet_counter = current_packet;
						globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
					}
					if(current_packet == expected_packets){
						// concatenate stream
						sensor_data = parent.sensor_types[80].utils.concat_fft_data(deviceAddr, payload[8]);
						// clear stream object & timeout
						parent.sensor_types[80].utils.clear_globalDevices_stream(deviceAddr);
						return sensor_data;
					}
					else{
						return;
					}
				}
				else if(payload[8] === 0 || payload[8] === 2){
					// mode byte most significant bit will indicate fft data.
					// console.log(d);
					var odr;
					switch(payload[9]){
						case 6:
							odr = "50Hz"
							break;
						case 7:
							odr = "100Hz";
							break;
						case 8:
							odr = "200Hz";
							break;
						case 9:
							odr = "400Hz";
							break;
						case 10:
							odr = "800Hz";
							break;
						case 11:
							odr = "1600Hz";
							break;
						case 12:
							odr = "3200Hz";
							break;
						case 13:
							odr = "6400Hz";
							break;
						case 14:
							odr = "12800Hz";
							break;
						case 15:
							odr = "25600Hz";
							break;
					}
					return {
						mode: payload[8],

						odr: odr,
						temperature: signInt(payload.slice(10, 12).reduce(msbLsb), 16) / 100,

						x_rms_ACC_G: payload.slice(12, 14).reduce(msbLsb)/1000,
						x_max_ACC_G: payload.slice(14, 16).reduce(msbLsb)/1000,
						x_velocity_mm_sec: payload.slice(16, 18).reduce(msbLsb) / 100,
						x_displacement_mm: payload.slice(18, 20).reduce(msbLsb) / 100,
						x_peak_one_Hz: payload.slice(20, 22).reduce(msbLsb),
						x_peak_two_Hz: payload.slice(22, 24).reduce(msbLsb),
						x_peak_three_Hz: payload.slice(24, 26).reduce(msbLsb),

						y_rms_ACC_G: payload.slice(26, 28).reduce(msbLsb)/1000,
						y_max_ACC_G: payload.slice(28, 30).reduce(msbLsb)/1000,
						y_velocity_mm_sec: payload.slice(30, 32).reduce(msbLsb) / 100,
						y_displacement_mm: payload.slice(32, 34).reduce(msbLsb) / 100,
						y_peak_one_Hz: payload.slice(34, 36).reduce(msbLsb),
						y_peak_two_Hz: payload.slice(36, 38).reduce(msbLsb),
						y_peak_three_Hz: payload.slice(38, 40).reduce(msbLsb),

						z_rms_ACC_G: payload.slice(40, 42).reduce(msbLsb)/1000,
						z_max_ACC_G: payload.slice(42, 44).reduce(msbLsb)/1000,
						z_velocity_mm_sec: payload.slice(44, 46).reduce(msbLsb) / 100,
						z_displacement_mm: payload.slice(46, 48).reduce(msbLsb) / 100,
						z_peak_one_Hz: payload.slice(48, 50).reduce(msbLsb),
						z_peak_two_Hz: payload.slice(50, 52).reduce(msbLsb),
						z_peak_three_Hz: payload.slice(52, 54).reduce(msbLsb),
					};
				}
				// else{
				// 	parsed.data = {'error': 'Vibration mode error'}
				// 	return parsed;
				// }
			},
			'utils': {
				'clear_globalDevices_stream': (deviceAddr) => {
					if(Object.hasOwn(globalDevices, deviceAddr)){
						if(Object.hasOwn(globalDevices[deviceAddr], 'packet_stream_timeout')){
							clearTimeout(globalDevices[deviceAddr].packet_stream_timeout);
						}
						delete globalDevices[deviceAddr];
					}
				},
				'init_globalDevices_stream'(deviceAddr, payload, expected_packets, parsed){
					switch(payload[9]){
						case 6:
							odr = 50;
							break;
						case 7:
							odr = 100;
							break;
						case 8:
							odr = 200;
							break;
						case 9:
							odr = 400;
							break;
						case 10:
							odr = 800;
							break;
						case 11:
							odr = 1600;
							break;
						case 12:
							odr = 3200;
							break;
						case 13:
							odr = 6400;
							break;
						case 14:
							odr = 12800;
							break;
						case 15:
							odr = 25600;
							break;
						default:
							odr = 0;
					}
					globalDevices[deviceAddr] = {
						data: {},
						odr: odr,
						mo: payload[8],
						fsr: payload[10] >> 5,
						en_axis: payload[10] & 7,
						hour: payload[11],
						minute: payload[12],
						temperature: msbLsb(payload[13], payload[14]) / 100,
						expected_packets: expected_packets
					}
					globalDevices[deviceAddr].packet_stream_timeout = setTimeout(() => {
						parsed.sensor_data = parent.sensor_types[80].utils.concat_fft_data(deviceAddr, payload[8]);
						parsed.sensor_data.error = 'Time Series Data Stream Timeout - incomplete data received';

						parent._emitter.emit('sensor_data', parsed);
						parent._emitter.emit('sensor_data-80', parsed);
						parent._emitter.emit('sensor_data'+'-'+deviceAddr, parsed);
					}, 60000);
				},
				'concat_fft_data': (deviceAddr) => {
					var raw_data = new Array();
					for(const packet in globalDevices[deviceAddr].data){
						raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
					}
					var label = 0;
					var fft_concat = {};

					var en_axis_data = {};
					switch (globalDevices[deviceAddr].en_axis){
						case 1:
							en_axis_data.x_offset = 0;
							en_axis_data.increment = 2;
							break;
						case 2:
							en_axis_data.y_offset = 0;
							en_axis_data.increment = 2;
							break;
						case 3:
							en_axis_data.x_offset = 0;
							en_axis_data.y_offset = 2;
							en_axis_data.increment = 4;
							break;
						case 4:
							en_axis_data.z_offset = 0;
							en_axis_data.increment = 2;
							break;
						case 5:
							en_axis_data.x_offset = 0;
							en_axis_data.z_offset = 2;
							en_axis_data.increment = 4;
							break;
						case 6:
							en_axis_data.y_offset = 0;
							en_axis_data.z_offset = 2;
							en_axis_data.increment = 4;
							break;
						case 7:
							en_axis_data.x_offset = 0;
							en_axis_data.y_offset = 2;
							en_axis_data.z_offset = 4;
							en_axis_data.increment = 6;
							break;
						default:
							en_axis_data.increment = 0;
					}
					
					var fsr_mult = .00006;
					var fsr_text = "";

					switch(globalDevices[deviceAddr].fsr){
						case 0:
							fsr_mult = 0.00006;
							break;
						case 1:
							fsr_mult = 0.00012;
							break;
						case 2:
							fsr_mult = 0.00024;
							break;
						case 3:
							fsr_mult = 0.00049;
							break;
					}
					switch(globalDevices[deviceAddr].fsr){
						case 0:
							fsr_text = "2g";
							break;
						case 1:
							fsr_text = "4g";
							break;
						case 2:
							fsr_text = "8g";
							break;
						case 3:
							fsr_text = "16g";
							break;
					}

					for(var i = 0; i < raw_data.length; i+=en_axis_data.increment){
						label++;

						fft_concat[label] = {};

						if('x_offset' in en_axis_data){
							fft_concat[label].x = parseFloat((signInt(((raw_data[i+en_axis_data.x_offset]<<8)+(raw_data[i+en_axis_data.x_offset+1])), 16)*fsr_mult).toFixed(2));
						}
						if('y_offset' in en_axis_data){
							fft_concat[label].y = parseFloat((signInt(((raw_data[i+en_axis_data.y_offset]<<8)+(raw_data[i+en_axis_data.y_offset+1])), 16)*fsr_mult).toFixed(2));
						}
						if('z_offset' in en_axis_data){
							fft_concat[label].z = parseFloat((signInt(((raw_data[i+en_axis_data.z_offset]<<8)+(raw_data[i+en_axis_data.z_offset+1])), 16)*fsr_mult).toFixed(2));
						}
					}

					var fft_concat_obj = {
						time_id: [
							String(globalDevices[deviceAddr].hour).padStart(2, '0'),
							String(globalDevices[deviceAddr].minute).padStart(2, '0'),
						].join(':'),
						mac_address: deviceAddr,
						en_axis: globalDevices[deviceAddr].en_axis,
						fsr: fsr_text,
						odr: globalDevices[deviceAddr].odr,
						device_temp: globalDevices[deviceAddr].temperature,
						// total_samples: label,
						fft_confidence : ((Object.keys(globalDevices[deviceAddr].data).length / globalDevices[deviceAddr].expected_packets) * 100).toFixed(2) + '%',
						data: fft_concat
					};
					return fft_concat_obj;
				}
			},
			'parse_fly': (frame) => {
				let frame_data = {};
				switch(frame[16]){
					case 0:
						frame_data.mode = "Processed";
						break;
					case 1:
						frame_data.mode = "Raw";
						break;
					case 2:
						frame_data.mode = "Processed + Raw on demand";
						break;
				}
				switch(frame[17]){
					case 6:
						frame_data.odr_1 = 50;
						break;
					case 7:
						frame_data.odr_1 = 100;
						break;
					case 8:
						frame_data.odr_1 = 200;
						break;
					case 9:
						frame_data.odr_1 = 400;
						break;
					case 10:
						frame_data.odr_1 = 800;
						break;
					case 11:
						frame_data.odr_1 = 1600;
						break;
					case 12:
						frame_data.odr_1 = 3200;
						break;
					case 13:
						frame_data.odr_1 = 6400;
						break;
					case 14:
						frame_data.odr_1 = 12800;
						break;
					case 15:
						frame_data.odr_1 = 25600;
						break;
				}
				frame_data.sampling_duration_1 = frame[19]*50 + "ms";
				switch(frame[21]){
					case 0:
						frame_data.filter_status = "Disabled";
						break;
					case 1:
						frame_data.filter_status = "Enabled";
						break;
				}
				switch(frame[22]){
					case 0:
						frame_data.lpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.lpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.lpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.lpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.lpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.lpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.lpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.lpf_coeff_1 = 2048;
						break;
				}
				frame_data.lpf_freq_1 = frame_data.odr_1 / frame_data.lpf_coeff_1;
				switch(frame[24]){
					case 0:
						frame_data.hpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.hpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.hpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.hpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.hpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.hpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.hpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.hpf_coeff_1 = 2048;
						break;
				}
				frame_data.hpf_freq_1 = frame_data.odr_1 / frame_data.hpf_coeff_1;
				switch(frame[26]){
					case 0:
						frame_data.sampling_interval = "5 Minutes";
						break;
					case 1:
						frame_data.sampling_interval = "10 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "15 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "20 Minutes";
						break;
					case 4:
						frame_data.sampling_interval = "30 Minutes";
						break;
					case 5:
						frame_data.sampling_interval = "60 Minutes";
						break;
					case 6:
						frame_data.sampling_interval = "120 Minutes";
						break;
					case 7:
						frame_data.sampling_interval = "180 Minutes";
						break;
					case 8:
						frame_data.sampling_interval = "1 Minute";
						break;
				}
				frame_data.on_request_timeout = frame[27] + " Seconds";
				frame_data.deadband = frame[28] + "mg";

				switch(frame[29]){
					case 0:
						frame_data.payload_length = "50 Bytes";
						break;
					case 1:
						frame_data.payload_length = "100 Bytes";
						break;
					case 2:
						frame_data.payload_length = "150 Bytes";
						break;
					case 3:
						frame_data.payload_length = "180 Bytes";
						break;
				}

				let response = {
					'firmware': frame[2],
					'destination_address': toMac(frame.slice(12, 16)),
					'mode': frame_data.mode,
					'odr_1': frame_data.odr_1+'Hz',
					'sampling_duration_1': frame_data.sampling_duration_1,
					'filter_status': frame_data.filter_status,
					'lpf_coeff_1': frame_data.lpf_coeff_1,
					'lpf_freq_1': frame_data.lpf_freq_1+'Hz',
					'hpf_coeff_1': frame_data.hpf_coeff_1,
					'hpf_freq_1': frame_data.hpf_freq_1+'Hz',
					'sampling_interval': frame_data.sampling_interval,
					'on_request_timeout': frame_data.on_request_timeout,
					'deadband': frame_data.deadband,
					'payload_length': frame_data.payload_length
				}
				if(response.firmware > 19){
					response.lifetime_transmissions = frame.slice(30, 34).reduce(msbLsb);
				}
				response.machine_values = {
					'firmware': frame[2],
					'destination_address': toMac(frame.slice(12, 16), false),
					'mode': frame[16],
					'odr_1': frame[17],
					'sampling_duration_1': frame[19],
					'filter_status': frame[21],
					'lpf_coeff_1': frame[22],
					'hpf_coeff_1': frame[24],
					'sampling_interval': frame[26],
					'on_request_timeout': frame[27],
					'deadband': frame[28],
					'payload_length': frame[29]
				}
				if(response.firmware > 19){
					response.machine_values.lifetime_transmissions = frame.slice(30, 34);
				}
				return response;
			}
		},
		'81': {
			name: 'Two Channel Vibration Plus',
			parse: (payload, parsed, mac) => {
				parsed.data = {};
				if(payload[7] & 2){
					parsed.data['probe_1_error'] = true;
				}
				if(payload[7] & 4){
					parsed.data['probe_2_error'] = true;
				}
				if(payload[7] & 2 && payload[7] & 4){
					return parsed;
				}

				if(payload[8] === 1){
					var deviceAddr = mac;
					var expected_packets = payload[15];
					var current_packet = payload[16];
					var sdata_start = 17;
					
					// If 4th bit is 1 the packet is from the second probe, if 0 from the first
					var probe;
					if(payload[7] & 8){
						probe = 2;
					}
					else{
						probe = 1;
					}
					// initialize globalDevices for this sensor if it doesn't exist
					// exlusively for multi-probe sensors
					if(!Object.hasOwn(globalDevices, deviceAddr)){
						globalDevices[deviceAddr] = {};
					}
					if(globalDevices[deviceAddr].hasOwnProperty(probe) || expected_packets == 1){
						console.log('Continuing existing probe stream');
						if (expected_packets != 1) {
							console.log('Expected packets more than 1');
							// if current packet is equal to last one (duplicated data). This does not apply to the last package
							if (globalDevices[deviceAddr][probe].last_packet_counter == current_packet) {
								console.log('Duplicated message')
								return;
							}
							// if current packet is equal to 1 or last packet counter is higher thant current packet
							if (current_packet == 1 || (globalDevices[deviceAddr][probe].last_packet_counter > current_packet)) {
								console.log('Recovering bad packet');
								// clear stream object & timeout
								parent.sensor_types[81].utils.clear_globalDevices_stream(deviceAddr, probe);

								// init new stream
								parent.sensor_types[81].utils.init_globalDevices_stream(deviceAddr, payload, expected_packets, parsed, probe);
								
								globalDevices[deviceAddr][probe].last_packet_counter = current_packet;
								globalDevices[deviceAddr][probe].data[current_packet] = payload.slice(sdata_start);
								return;
							} else {
								globalDevices[deviceAddr][probe].last_packet_counter = current_packet;
								globalDevices[deviceAddr][probe].data[current_packet] = payload.slice(sdata_start);
							}
						} else {
							// clear stream object & timeout
							parent.sensor_types[81].utils.clear_globalDevices_stream(deviceAddr, probe);
				
							// init new stream
							parent.sensor_types[81].utils.init_globalDevices_stream(deviceAddr, payload, expected_packets, parsed, probe);
				
							globalDevices[deviceAddr][probe].last_packet_counter = current_packet;
							globalDevices[deviceAddr][probe].data[current_packet] = payload.slice(sdata_start);
						}
					}
					else{
						// clear stream object & timeout
						parent.sensor_types[81].utils.clear_globalDevices_stream(deviceAddr, probe);

						// init new stream
						parent.sensor_types[81].utils.init_globalDevices_stream(deviceAddr, payload, expected_packets, parsed, probe);

						globalDevices[deviceAddr][probe].last_packet_counter = current_packet;
						globalDevices[deviceAddr][probe].data[current_packet] = payload.slice(sdata_start);
					}
					if(current_packet == expected_packets){
						// concatenate stream
						sensor_data = parent.sensor_types[81].utils.concat_fft_data(deviceAddr, payload[8], probe);

						// clear stream object & timeout
						parent.sensor_types[81].utils.clear_globalDevices_stream(deviceAddr, probe);
						return sensor_data;
					}
					else{
						return;
					}
				}
				else if(payload[8] === 0 || payload[8] === 2){
					// mode byte most significant bit will indicate fft data.
					// console.log(d);
					var odr1;
					switch(payload[9]){
						case 6:
							odr1 = "50Hz"
							break;
						case 7:
							odr1 = "100Hz";
							break;
						case 8:
							odr1 = "200Hz";
							break;
						case 9:
							odr1 = "400Hz";
							break;
						case 10:
							odr1 = "800Hz";
							break;
						case 11:
							odr1 = "1600Hz";
							break;
						case 12:
							odr1 = "3200Hz";
							break;
						case 13:
							odr1 = "6400Hz";
							break;
						case 14:
							odr1 = "12800Hz";
							break;
						case 15:
							odr1 = "25600Hz";
							break;
					}
					var odr2;
					switch(payload[54]){
						case 6:
							odr2 = "50Hz"
							break;
						case 7:
							odr2 = "100Hz";
							break;
						case 8:
							odr2 = "200Hz";
							break;
						case 9:
							odr2 = "400Hz";
							break;
						case 10:
							odr2 = "800Hz";
							break;
						case 11:
							odr2 = "1600Hz";
							break;
						case 12:
							odr2 = "3200Hz";
							break;
						case 13:
							odr2 = "6400Hz";
							break;
						case 14:
							odr2 = "12800Hz";
							break;
						case 15:
							odr2 = "25600Hz";
							break;
					}

					// If 4th bit is 1 the packet is from the second probe, if 0 from the first
					// var probe = '';
					// if(payload[7] & 8){
					// 	probe = '2';
					// }
					// else{
					// 	probe = '1';
					// }

					return {
						mode: payload[8],

						s1_odr: odr1,
						s1_temperature: signInt(payload.slice(10, 12).reduce(msbLsb), 16) / 100,

						x1_rms_ACC_G: payload.slice(12, 14).reduce(msbLsb)/1000,
						x1_max_ACC_G: payload.slice(14, 16).reduce(msbLsb)/1000,
						x1_velocity_mm_sec: payload.slice(16, 18).reduce(msbLsb) / 100,
						x1_displacement_mm: payload.slice(18, 20).reduce(msbLsb) / 100,
						x1_peak_one_Hz: payload.slice(20, 22).reduce(msbLsb),
						x1_peak_two_Hz: payload.slice(22, 24).reduce(msbLsb),
						x1_peak_three_Hz: payload.slice(24, 26).reduce(msbLsb),

						y1_rms_ACC_G: payload.slice(26, 28).reduce(msbLsb)/1000,
						y1_max_ACC_G: payload.slice(28, 30).reduce(msbLsb)/1000,
						y1_velocity_mm_sec: payload.slice(30, 32).reduce(msbLsb) / 100,
						y1_displacement_mm: payload.slice(32, 34).reduce(msbLsb) / 100,
						y1_peak_one_Hz: payload.slice(34, 36).reduce(msbLsb),
						y1_peak_two_Hz: payload.slice(36, 38).reduce(msbLsb),
						y1_peak_three_Hz: payload.slice(38, 40).reduce(msbLsb),

						z1_rms_ACC_G: payload.slice(40, 42).reduce(msbLsb)/1000,
						z1_max_ACC_G: payload.slice(42, 44).reduce(msbLsb)/1000,
						z1_velocity_mm_sec: payload.slice(44, 46).reduce(msbLsb) / 100,
						z1_displacement_mm: payload.slice(46, 48).reduce(msbLsb) / 100,
						z1_peak_one_Hz: payload.slice(48, 50).reduce(msbLsb),
						z1_peak_two_Hz: payload.slice(50, 52).reduce(msbLsb),
						z1_peak_three_Hz: payload.slice(52, 54).reduce(msbLsb),

						s2_odr: odr2,
						s2_temperature: signInt(payload.slice(55, 57).reduce(msbLsb), 16) / 100,

						x2_rms_ACC_G: payload.slice(57, 59).reduce(msbLsb)/1000,
						x2_max_ACC_G: payload.slice(59, 61).reduce(msbLsb)/1000,
						x2_velocity_mm_sec: payload.slice(61, 63).reduce(msbLsb) / 100,
						x2_displacement_mm: payload.slice(63, 65).reduce(msbLsb) / 100,
						x2_peak_one_Hz: payload.slice(65, 67).reduce(msbLsb),
						x2_peak_two_Hz: payload.slice(67, 69).reduce(msbLsb),
						x2_peak_three_Hz: payload.slice(69, 71).reduce(msbLsb),

						y2_rms_ACC_G: payload.slice(71, 73).reduce(msbLsb)/1000,
						y2_max_ACC_G: payload.slice(73, 75).reduce(msbLsb)/1000,
						y2_velocity_mm_sec: payload.slice(75, 77).reduce(msbLsb) / 100,
						y2_displacement_mm: payload.slice(77, 79).reduce(msbLsb) / 100,
						y2_peak_one_Hz: payload.slice(79, 81).reduce(msbLsb),
						y2_peak_two_Hz: payload.slice(81, 83).reduce(msbLsb),
						y2_peak_three_Hz: payload.slice(83, 85).reduce(msbLsb),

						z2_rms_ACC_G: payload.slice(85, 87).reduce(msbLsb)/1000,
						z2_max_ACC_G: payload.slice(87, 89).reduce(msbLsb)/1000,
						z2_velocity_mm_sec: payload.slice(89, 91).reduce(msbLsb) / 100,
						z2_displacement_mm: payload.slice(91, 93).reduce(msbLsb) / 100,
						z2_peak_one_Hz: payload.slice(93, 95).reduce(msbLsb),
						z2_peak_two_Hz: payload.slice(95, 97).reduce(msbLsb),
						z2_peak_three_Hz: payload.slice(97, 99).reduce(msbLsb)
					};
				}
				// else{
				// 	parsed.data = {'error': 'Vibration mode error'}
				// 	return parsed;
				// }
			},
			'utils': {
				'clear_globalDevices_stream': (deviceAddr, probe) => {
					if(Object.hasOwn(globalDevices, deviceAddr) && Object.hasOwn(globalDevices[deviceAddr], probe)){
						if(Object.hasOwn(globalDevices[deviceAddr][probe], 'packet_stream_timeout')){
							clearTimeout(globalDevices[deviceAddr][probe].packet_stream_timeout);
						}
						delete globalDevices[deviceAddr][probe];
					}
				},
				'init_globalDevices_stream'(deviceAddr, payload, expected_packets, parsed, probe){
					var odr = payload[9];
					switch(odr){
						case 6:
							odr = 50;
							break;
						case 7:
							odr = 100;
							break;
						case 8:
							odr = 200;
							break;
						case 9:
							odr = 400;
							break;
						case 10:
							odr = 800;
							break;
						case 11:
							odr = 1600;
							break;
						case 12:
							odr = 3200;
							break;
						case 13:
							odr = 6400;
							break;
						case 14:
							odr = 12800;
							break;
						case 15:
							odr = 25600;
							break;
						default:
							odr = 0;
					}
					globalDevices[deviceAddr][probe] = {
						data: {},
						odr: odr,
						mo: payload[8],
						fsr: payload[10] >> 5,
						en_axis: payload[10] & 7,
						hour: payload[11],
						minute: payload[12],
						temperature: msbLsb(payload[13], payload[14]) / 100,
						expected_packets: expected_packets
					}
					globalDevices[deviceAddr][probe].packet_stream_timeout = setTimeout(() => {
						parsed.sensor_data = parent.sensor_types[81].utils.concat_fft_data(deviceAddr, payload[8], probe);
						parsed.sensor_data.error = 'Time Series Data Stream Timeout - incomplete data received';

						parsed.sensor_name = parent.sensor_types[81].name;
						parsed.type = 'sensor_data';
						parsed.addr = deviceAddr;
						parsed.received = Date.now();

						parent._emitter.emit('sensor_data', parsed);
						parent._emitter.emit('sensor_data-81', parsed);
						parent._emitter.emit('sensor_data'+'-'+deviceAddr, parsed);
					}, 60000);
				},
				'concat_fft_data': (deviceAddr, mode, probe) => {
					var raw_data = new Array();
					for(const packet in globalDevices[deviceAddr][probe].data){
						raw_data = raw_data.concat(globalDevices[deviceAddr][probe].data[packet]);
					}
					var label = 0;
					var fft_concat = {};

					var en_axis_data = {};
					switch (globalDevices[deviceAddr][probe].en_axis){
						case 1:
							en_axis_data.x_offset = 0;
							en_axis_data.increment = 2;
							break;
						case 2:
							en_axis_data.y_offset = 0;
							en_axis_data.increment = 2;
							break;
						case 3:
							en_axis_data.x_offset = 0;
							en_axis_data.y_offset = 2;
							en_axis_data.increment = 4;
							break;
						case 4:
							en_axis_data.z_offset = 0;
							en_axis_data.increment = 2;
							break;
						case 5:
							en_axis_data.x_offset = 0;
							en_axis_data.z_offset = 2;
							en_axis_data.increment = 4;
							break;
						case 6:
							en_axis_data.y_offset = 0;
							en_axis_data.z_offset = 2;
							en_axis_data.increment = 4;
							break;
						case 7:
							en_axis_data.x_offset = 0;
							en_axis_data.y_offset = 2;
							en_axis_data.z_offset = 4;
							en_axis_data.increment = 6;
							break;
						default:
							en_axis_data.increment = 0;
					}
					
					var fsr_mult = .00006;
					var fsr_text = "";

					switch(globalDevices[deviceAddr][probe].fsr){
						case 0:
							fsr_mult = 0.00006;
							break;
						case 1:
							fsr_mult = 0.00012;
							break;
						case 2:
							fsr_mult = 0.00024;
							break;
						case 3:
							fsr_mult = 0.00049;
							break;
					}
					switch(globalDevices[deviceAddr][probe].fsr){
						case 0:
							fsr_text = "2g";
							break;
						case 1:
							fsr_text = "4g";
							break;
						case 2:
							fsr_text = "8g";
							break;
						case 3:
							fsr_text = "16g";
							break;
					}

					for(var i = 0; i < raw_data.length; i+=en_axis_data.increment){
						label++;

						fft_concat[label] = {};

						if('x_offset' in en_axis_data){
							fft_concat[label].x = parseFloat((signInt(((raw_data[i+en_axis_data.x_offset]<<8)+(raw_data[i+en_axis_data.x_offset+1])), 16)*fsr_mult).toFixed(2));
						}
						if('y_offset' in en_axis_data){
							fft_concat[label].y = parseFloat((signInt(((raw_data[i+en_axis_data.y_offset]<<8)+(raw_data[i+en_axis_data.y_offset+1])), 16)*fsr_mult).toFixed(2));
						}
						if('z_offset' in en_axis_data){
							fft_concat[label].z = parseFloat((signInt(((raw_data[i+en_axis_data.z_offset]<<8)+(raw_data[i+en_axis_data.z_offset+1])), 16)*fsr_mult).toFixed(2));
						}
					}

					var fft_concat_obj = {
						probe: probe,
						time_id: [
							String(globalDevices[deviceAddr][probe].hour).padStart(2, '0'),
							String(globalDevices[deviceAddr][probe].minute).padStart(2, '0'),
						].join(':'),
						mac_address: deviceAddr,
						en_axis: globalDevices[deviceAddr][probe].en_axis,
						fsr: fsr_text,
						odr: globalDevices[deviceAddr][probe].odr,
						device_temp: globalDevices[deviceAddr][probe].temperature,
						// total_samples: label,
						fft_confidence : ((Object.keys(globalDevices[deviceAddr][probe].data).length / globalDevices[deviceAddr][probe].expected_packets) * 100).toFixed(2) + '%',
						data: fft_concat
					};
					return fft_concat_obj;
				}
			},
			'parse_fly': (frame) => {
				let frame_data = {};
				switch(frame[16]){
					case 0:
						frame_data.mode = "Processed";
						break;
					case 1:
						frame_data.mode = "Raw";
						break;
					case 2:
						frame_data.mode = "Processed + Raw on demand";
						break;
				}
				switch(frame[17]){
					case 6:
						frame_data.odr_1 = 50;
						break;
					case 7:
						frame_data.odr_1 = 100;
						break;
					case 8:
						frame_data.odr_1 = 200;
						break;
					case 9:
						frame_data.odr_1 = 400;
						break;
					case 10:
						frame_data.odr_1 = 800;
						break;
					case 11:
						frame_data.odr_1 = 1600;
						break;
					case 12:
						frame_data.odr_1 = 3200;
						break;
					case 13:
						frame_data.odr_1 = 6400;
						break;
					case 14:
						frame_data.odr_1 = 12800;
						break;
					case 15:
						frame_data.odr_1 = 25600;
						break;
				}
				switch(frame[18]){
					case 6:
						frame_data.odr_2 = 50;
						break;
					case 7:
						frame_data.odr_2 = 100;
						break;
					case 8:
						frame_data.odr_2 = 200;
						break;
					case 9:
						frame_data.odr_2 = 400;
						break;
					case 10:
						frame_data.odr_2 = 800;
						break;
					case 11:
						frame_data.odr_2 = 1600;
						break;
					case 12:
						frame_data.odr_2 = 3200;
						break;
					case 13:
						frame_data.odr_2 = 6400;
						break;
					case 14:
						frame_data.odr_2 = 12800;
						break;
					case 15:
						frame_data.odr_2 = 25600;
						break;
				}
				frame_data.sampling_duration_1 = frame[19]*50 + "ms";
				frame_data.sampling_duration_2 = frame[20]*50 + "ms";
				switch(frame[21]){
					case 0:
						frame_data.filter_status = "Disabled";
						break;
					case 1:
						frame_data.filter_status = "Enabled";
						break;
				}
				switch(frame[22]){
					case 0:
						frame_data.lpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.lpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.lpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.lpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.lpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.lpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.lpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.lpf_coeff_1 = 2048;
						break;
				}
				frame_data.lpf_freq_1 = frame_data.odr_1 / frame_data.lpf_coeff_1;
				switch(frame[23]){
					case 0:
						frame_data.lpf_coeff_2 = 4;
						break;
					case 1:
						frame_data.lpf_coeff_2 = 8;
						break;
					case 2:
						frame_data.lpf_coeff_2 = 16;
						break;
					case 2:
						frame_data.lpf_coeff_2 = 32;
						break;
					case 4:
						frame_data.lpf_coeff_2 = 64;
						break;
					case 5:
						frame_data.lpf_coeff_2 = 128;
						break;
					case 6:
						frame_data.lpf_coeff_2 = 256;
						break;
					case 7:
						frame_data.lpf_coeff_2 = 512;
						break;
					case 8:
						frame_data.lpf_coeff_2 = 1024;
						break;
					case 9:
						frame_data.lpf_coeff_2 = 2048;
						break;
				}
				frame_data.lpf_freq_2 = frame_data.odr_2 / frame_data.lpf_coeff_2;
				switch(frame[24]){
					case 0:
						frame_data.hpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.hpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.hpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.hpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.hpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.hpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.hpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.hpf_coeff_1 = 2048;
						break;
				}
				frame_data.hpf_freq_1 = frame_data.odr_1 / frame_data.hpf_coeff_1;
				switch(frame[25]){
					case 0:
						frame_data.hpf_coeff_2 = 4;
						break;
					case 1:
						frame_data.hpf_coeff_2 = 8;
						break;
					case 2:
						frame_data.hpf_coeff_2 = 16;
						break;
					case 2:
						frame_data.hpf_coeff_2 = 32;
						break;
					case 4:
						frame_data.hpf_coeff_2 = 64;
						break;
					case 5:
						frame_data.hpf_coeff_2 = 128;
						break;
					case 6:
						frame_data.hpf_coeff_2 = 256;
						break;
					case 7:
						frame_data.hpf_coeff_2 = 512;
						break;
					case 8:
						frame_data.hpf_coeff_2 = 1024;
						break;
					case 9:
						frame_data.hpf_coeff_2 = 2048;
						break;
				}
				frame_data.hpf_freq_2 = frame_data.odr_2 / frame_data.hpf_coeff_2;
				switch(frame[26]){
					case 0:
						frame_data.sampling_interval = "5 Minutes";
						break;
					case 1:
						frame_data.sampling_interval = "10 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "15 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "20 Minutes";
						break;
					case 4:
						frame_data.sampling_interval = "30 Minutes";
						break;
					case 5:
						frame_data.sampling_interval = "60 Minutes";
						break;
					case 6:
						frame_data.sampling_interval = "120 Minutes";
						break;
					case 7:
						frame_data.sampling_interval = "180 Minutes";
						break;
					case 8:
						frame_data.sampling_interval = "1 Minute";
						break;
				}
				frame_data.on_request_timeout = frame[27] + " Seconds";
				frame_data.deadband = frame[28] + "mg";

				switch(frame[29]){
					case 0:
						frame_data.payload_length = "50 Bytes";
						break;
					case 1:
						frame_data.payload_length = "100 Bytes";
						break;
					case 2:
						frame_data.payload_length = "150 Bytes";
						break;
					case 3:
						frame_data.payload_length = "180 Bytes";
						break;
				}

				let response = {
					'firmware': frame[2],
					'destination_address': toMac(frame.slice(12, 16)),
					'mode': frame_data.mode,
					'odr_1': frame_data.odr_1+'Hz',
					'odr_2': frame_data.odr_2+'Hz',
					'sampling_duration_1': frame_data.sampling_duration_1,
					'sampling_duration_2': frame_data.sampling_duration_2,
					'filter_status': frame_data.filter_status,
					'lpf_coeff_1': frame_data.lpf_coeff_1,
					'lpf_freq_1': frame_data.lpf_freq_1+'Hz',
					'hpf_coeff_1': frame_data.hpf_coeff_1,
					'hpf_freq_1': frame_data.hpf_freq_1+'Hz',
					'lpf_coeff_2': frame_data.lpf_coeff_2,
					'lpf_freq_2': frame_data.lpf_freq_2+'Hz',
					'hpf_coeff_2': frame_data.hpf_coeff_2,
					'hpf_freq_2': frame_data.hpf_freq_2+'Hz',
					'sampling_interval': frame_data.sampling_interval,
					'on_request_timeout': frame_data.on_request_timeout,
					'deadband': frame_data.deadband,
					'payload_length': frame_data.payload_length
				}
				if(response.firmware > 19){
					response.lifetime_transmissions = frame.slice(30, 34).reduce(msbLsb);
				}
				response.machine_values = {
					'firmware': frame[2],
					'destination_address': toMac(frame.slice(12, 16), false),
					'mode': frame[16],
					'odr_1': frame[17],
					'odr_2': frame[18],
					'sampling_duration_1': frame[19],
					'sampling_duration_2': frame[20],
					'filter_status': frame[21],
					'lpf_coeff_1': frame[22],
					'lpf_coeff_2': frame[23],
					'hpf_coeff_1': frame[24],
					'hpf_coeff_2': frame[25],
					'sampling_interval': frame[26],
					'on_request_timeout': frame[27],
					'deadband': frame[28],
					'payload_length': frame[29]
				}
				if(response.firmware > 19){
					response.machine_values.lifetime_transmissions = frame.slice(30, 34);
				}
				return response;
			}
		},

		'82': {
			name: 'Condition Based/Predictive Maintenance Sensor',
			parse: (payload, parsed, mac) => {
				if(payload[7] >> 1 != 0){
					console.log('Error found');
					console.log(payload[7]);
					parsed.data = {error: 'Error found, Sensor Probe may be unattached'};
					return parsed;
				}

				if(payload[8] === 1){
					var deviceAddr = mac;
					var firmware = payload[1];
					var mode = payload[8];
					var odr = payload[9];
					var en_axis = payload[10] & 7;
					var fsr = payload[10] >> 5;
					var hour = payload[11];
					var minute = payload[12];
					var device_temp = msbLsb(payload[13], payload[14])/100;
					var expected_packets = payload[15];
					var current_packet = payload[16];
					var sdata_start = 17;

					switch(odr){
						case 6:
							odr = 50;
							break;
						case 7:
							odr = 100;
							break;
						case 8:
							odr = 200;
							break;
						case 9:
							odr = 400;
							break;
						case 10:
							odr = 800;
							break;
						case 11:
							odr = 1600;
							break;
						case 12:
							odr = 3200;
							break;
						case 13:
							odr = 6400;
							break;
						case 14:
							odr = 12800;
							break;
						case 15:
							odr = 25600;
							break;
						default:
							odr = 0;
					}

					if(globalDevices.hasOwnProperty(deviceAddr) || expected_packets == 1){
						if(expected_packets != 1){
							// if a packet is already stored with the same packet ID, or if packet ID is 1, or if current packet ID is not one more than last packet ID
							if(current_packet in globalDevices[deviceAddr].data || current_packet == 1 || !((current_packet-1) in globalDevices[deviceAddr].data)) {
								console.log('-----');
								console.log('bad packet breakdown deleting stream');
								console.log(current_packet);
								console.log(expected_packets);
								console.log(current_packet in globalDevices[deviceAddr].data);
								console.log(current_packet == 1);
								console.log(!((current_packet-1) in globalDevices[deviceAddr].data));
								if(this.hasOwnProperty('failure_no')){
									this.failure_no = this.failure_no + 1;
								}
								else{
									this.failure_no = 1;
								}
								if(this.hasOwnProperty('failure_no')){
									console.log('####falure no');
									console.log(this.failure_no);
								}
								// console.log(globalDevices[deviceAddr].data);
								delete globalDevices[deviceAddr];
								if(current_packet != 1){
									return;
								} else{

									globalDevices[deviceAddr] = {
										// stream_size: expected_packets,
										data: {},
										odr: odr,
										mo: mode,
										en_axis: en_axis,
										fsr: fsr,
										hour: hour,
										minute: minute,
										device_temp: device_temp,
									}
									globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
									return;
								}
							}
							else{
								globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
							}
						}
						else{

							globalDevices[deviceAddr] = {
								// stream_size: expected_packets,
								data: {},
								odr: odr,
								mo: mode,
								en_axis: en_axis,
								fsr: fsr,
								hour: hour,
								minute: minute,
								device_temp: device_temp,
							}
							globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						}
						if(Object.keys(globalDevices[deviceAddr].data).length == expected_packets){
							var raw_data = new Array();
							for(const packet in globalDevices[deviceAddr].data){
								raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
							}
							var label = 0;

							var fft = new Array();
							var fft_concat = {};

							var en_axis_data = {};
							switch (globalDevices[deviceAddr].en_axis){
								case 1:
									en_axis_data.x_offset = 0;
									en_axis_data.increment = 2;
									break;
								case 2:
									en_axis_data.y_offset = 0;
									en_axis_data.increment = 2;
									break;
								case 3:
									en_axis_data.x_offset = 0;
									en_axis_data.y_offset = 2;
									en_axis_data.increment = 4;
									break;
								case 4:
									en_axis_data.z_offset = 0;
									en_axis_data.increment = 2;
									break;
								case 5:
									en_axis_data.x_offset = 0;
									en_axis_data.z_offset = 2;
									en_axis_data.increment = 4;
									break;
								case 6:
									en_axis_data.y_offset = 0;
									en_axis_data.z_offset = 2;
									en_axis_data.increment = 4;
									break;
								case 7:
									en_axis_data.x_offset = 0;
									en_axis_data.y_offset = 2;
									en_axis_data.z_offset = 4;
									en_axis_data.increment = 6;
									break;
								default:
									en_axis_data.increment = 0;
							}

							var fsr_mult = .00006;
							var fsr_text = "";
							switch(globalDevices[deviceAddr].fsr){
								case 0:
									fsr_mult = 0.00006;
									break;
								case 1:
									fsr_mult = 0.00012;
									break;
								case 2:
									fsr_mult = 0.00024;
									break;
								case 3:
									fsr_mult = 0.00049;
									break;
							}
							switch(globalDevices[deviceAddr].fsr){
								case 0:
									fsr_text = "2g";
									break;
								case 1:
									fsr_text = "4g";
									break;
								case 2:
									fsr_text = "8g";
									break;
								case 3:
									fsr_text = "16g";
									break;
							}

							for(var i = 0; i < raw_data.length; i+=en_axis_data.increment){
								label++;

								fft_concat[label] = {};

								if('x_offset' in en_axis_data){
									fft_concat[label].x = parseFloat((signInt(((raw_data[i+en_axis_data.x_offset]<<8)+(raw_data[i+en_axis_data.x_offset+1])), 16)*fsr_mult).toFixed(2));
								}
								if('y_offset' in en_axis_data){
									fft_concat[label].y = parseFloat((signInt(((raw_data[i+en_axis_data.y_offset]<<8)+(raw_data[i+en_axis_data.y_offset+1])), 16)*fsr_mult).toFixed(2));
								}
								if('z_offset' in en_axis_data){
									fft_concat[label].z = parseFloat((signInt(((raw_data[i+en_axis_data.z_offset]<<8)+(raw_data[i+en_axis_data.z_offset+1])), 16)*fsr_mult).toFixed(2));
								}
							}
							var fft_concat_obj = {
								time_id: globalDevices[deviceAddr].hour +':'+ globalDevices[deviceAddr].minute,
								mac_address: deviceAddr,
								en_axis: globalDevices[deviceAddr].en_axis,
								fsr: fsr_text,
								odr: globalDevices[deviceAddr].odr,
								device_temp: globalDevices[deviceAddr].device_temp,
								data: fft_concat
							};
							// console.log(globalDevices[deviceAddr].data);
							// console.log(raw_data);
							sensor_data = fft_concat_obj;
							// parsed.raw_packets = globalDevices[deviceAddr].data;
							// parsed.raw_data = raw_data;
							delete globalDevices[deviceAddr];
							if(this.hasOwnProperty('failure_no')){
								console.log('####falure no');
								console.log(this.failure_no);
							}

							return sensor_data;
						}
						else{
							return;
						}
					}else{

						globalDevices[deviceAddr] = {
							// stream_size: expected_packets,
							data: {},
							odr: odr,
							mo: mode,
							en_axis: en_axis,
							fsr: fsr,
							hour: hour,
							minute: minute,
							device_temp: device_temp,
						}
						globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						return;
					}
				}
				else if(payload[8] === 0 || payload[8] === 2){
					// mode byte most significant bit will indicate fft data.
					// console.log(d);
					var odr;
					switch(payload[9]){
						case 6:
							odr = "50Hz"
							break;
						case 7:
							odr = "100Hz";
							break;
						case 8:
							odr = "200Hz";
							break;
						case 9:
							odr = "400Hz";
							break;
						case 10:
							odr = "800Hz";
							break;
						case 11:
							odr = "1600Hz";
							break;
						case 12:
							odr = "3200Hz";
							break;
						case 13:
							odr = "6400Hz";
							break;
						case 14:
							odr = "12800Hz";
							break;
						case 15:
							odr = "25600Hz";
							break;
					}
					return {
						mode: payload[8],

						odr: odr,
						temperature: signInt(payload.slice(10, 12).reduce(msbLsb), 16) / 100,
						Ext_temperature: signInt(payload.slice(12, 16).reduce(msbLsb), 32) / 100,
						Current: signInt(payload.slice(16, 20).reduce(msbLsb), 32) / 1000,
						x_rms_ACC_G: payload.slice(20, 22).reduce(msbLsb)/1000,
						x_max_ACC_G: payload.slice(22, 24).reduce(msbLsb)/1000,
						x_velocity_mm_sec: payload.slice(24, 26).reduce(msbLsb) / 100,
						x_displacement_mm: payload.slice(26, 28).reduce(msbLsb) / 100,
						x_peak_one_Hz: payload.slice(28, 30).reduce(msbLsb),
						x_peak_two_Hz: payload.slice(30, 32).reduce(msbLsb),
						x_peak_three_Hz: payload.slice(32, 34).reduce(msbLsb),

						y_rms_ACC_G: payload.slice(34, 36).reduce(msbLsb)/1000,
						y_max_ACC_G: payload.slice(36, 38).reduce(msbLsb)/1000,
						y_velocity_mm_sec: payload.slice(38, 40).reduce(msbLsb) / 100,
						y_displacement_mm: payload.slice(40, 42).reduce(msbLsb) / 100,
						y_peak_one_Hz: payload.slice(42, 44).reduce(msbLsb),
						y_peak_two_Hz: payload.slice(44, 46).reduce(msbLsb),
						y_peak_three_Hz: payload.slice(46, 48).reduce(msbLsb),

						z_rms_ACC_G: payload.slice(48, 50).reduce(msbLsb)/1000,
						z_max_ACC_G: payload.slice(50, 52).reduce(msbLsb)/1000,
						z_velocity_mm_sec: payload.slice(52, 54).reduce(msbLsb) / 100,
						z_displacement_mm: payload.slice(54, 56).reduce(msbLsb) / 100,
						z_peak_one_Hz: payload.slice(56, 58).reduce(msbLsb),
						z_peak_two_Hz: payload.slice(58, 60).reduce(msbLsb),
						z_peak_three_Hz: payload.slice(60, 62).reduce(msbLsb),
					};
				}
				// else{
				// 	parsed.data = {'error': 'Vibration mode error'}
				// 	return parsed;
				// }
			},
			'parse_fly': (frame) => {
				let frame_data = {};
				switch(frame[16]){
					case 0:
						frame_data.mode = "Processed";
						break;
					case 1:
						frame_data.mode = "Raw";
						break;
					case 2:
						frame_data.mode = "Processed + Raw on demand";
						break;
				}
				switch(frame[17]){
					case 6:
						frame_data.odr_1 = 50;
						break;
					case 7:
						frame_data.odr_1 = 100;
						break;
					case 8:
						frame_data.odr_1 = 200;
						break;
					case 9:
						frame_data.odr_1 = 400;
						break;
					case 10:
						frame_data.odr_1 = 800;
						break;
					case 11:
						frame_data.odr_1 = 1600;
						break;
					case 12:
						frame_data.odr_1 = 3200;
						break;
					case 13:
						frame_data.odr_1 = 6400;
						break;
					case 14:
						frame_data.odr_1 = 12800;
						break;
					case 15:
						frame_data.odr_1 = 25600;
						break;
				}
				frame_data.sampling_duration_1 = frame[19]*50 + "ms";
				switch(frame[21]){
					case 0:
						frame_data.filter_status = "Disabled";
						break;
					case 1:
						frame_data.filter_status = "Enabled";
						break;
				}
				switch(frame[22]){
					case 0:
						frame_data.lpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.lpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.lpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.lpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.lpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.lpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.lpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.lpf_coeff_1 = 2048;
						break;
				}
				frame_data.lpf_freq_1 = frame_data.odr_1 / frame_data.lpf_coeff_1;
				switch(frame[24]){
					case 0:
						frame_data.hpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.hpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.hpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.hpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.hpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.hpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.hpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.hpf_coeff_1 = 2048;
						break;
				}
				frame_data.hpf_freq_1 = frame_data.odr_1 / frame_data.hpf_coeff_1;
				switch(frame[26]){
					case 0:
						frame_data.sampling_interval = "5 Minutes";
						break;
					case 1:
						frame_data.sampling_interval = "10 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "15 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "20 Minutes";
						break;
					case 4:
						frame_data.sampling_interval = "30 Minutes";
						break;
					case 5:
						frame_data.sampling_interval = "60 Minutes";
						break;
					case 6:
						frame_data.sampling_interval = "120 Minutes";
						break;
					case 7:
						frame_data.sampling_interval = "180 Minutes";
						break;
					case 8:
						frame_data.sampling_interval = "1 Minute";
						break;
				}
				frame_data.on_request_timeout = frame[27] + " Seconds";
				frame_data.deadband = frame[28] + "mg";

				switch(frame[29]){
					case 0:
						frame_data.payload_length = "50 Bytes";
						break;
					case 1:
						frame_data.payload_length = "100 Bytes";
						break;
					case 2:
						frame_data.payload_length = "150 Bytes";
						break;
					case 3:
						frame_data.payload_length = "180 Bytes";
						break;
				}

				return {
					'firmware': frame[2],
					'destination_address': toMac(frame.slice(12, 16)),
					'mode': frame_data.mode,
					'odr_1': frame_data.odr_1+'Hz',
					'sampling_duration_1': frame_data.sampling_duration_1,
					'filter_status': frame_data.filter_status,
					'lpf_coeff_1': frame_data.lpf_coeff_1,
					'lpf_freq_1': frame_data.lpf_freq_1+'Hz',
					'hpf_coeff_1': frame_data.hpf_coeff_1,
					'hpf_freq_1': frame_data.hpf_freq_1+'Hz',
					'sampling_interval': frame_data.sampling_interval,
					'on_request_timeout': frame_data.on_request_timeout,
					'deadband': frame_data.deadband,
					'payload_length': frame_data.payload_length,
					'machine_values': {
						'firmware': frame[2],
						'destination_address': toMac(frame.slice(12, 16), false),
						'mode': frame[16],
						'odr_1': frame[17],
						'sampling_duration_1': frame[19],
						'filter_status': frame[21],
						'lpf_coeff_1': frame[22],
						'hpf_coeff_1': frame[24],
						'sampling_interval': frame[26],
						'on_request_timeout': frame[27],
						'deadband': frame[28],
						'payload_length': frame[29]
					}
				}
			}
		},

		'84': {
			name: 'Type 84 - Standalone Smart Vibration Sensor',
			parse: (payload, parsed, mac) => {
				if(payload[7] >> 1 != 0){
					console.log('Error found');
					parsed.data = {error: 'Error found, Sensor Probe may be unattached'};
					return parsed;
				}

				if(payload[8] === 1){
					var deviceAddr = mac;
					var expected_packets = payload[15];
					var current_packet = payload[16];
					var sdata_start = 17;

					if(globalDevices.hasOwnProperty(deviceAddr) || expected_packets == 1){
						if(expected_packets != 1){
							// if current packet is equal to last one (duplicated data). This does not apply to the last package
							if (globalDevices[deviceAddr].last_packet_counter == current_packet){
								console.log('Duplicated message')
								return;
							}
							// if current packet is equal to 1 or last packet counter is higher thant current packet
							if(current_packet == 1 || (globalDevices[deviceAddr].last_packet_counter > current_packet)){
								console.log('Recovering bad packet');
								// clear stream object & timeout
								parent.sensor_types[84].utils.clear_globalDevices_stream(deviceAddr);

								// init new stream
								parent.sensor_types[84].utils.init_globalDevices_stream(deviceAddr, payload, expected_packets, parsed);

								globalDevices[deviceAddr].last_packet_counter = current_packet;
								globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
								return;
							}
							else{
								globalDevices[deviceAddr].last_packet_counter = current_packet;
								globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
							}
						}
						else{
							// clear stream object & timeout
							parent.sensor_types[84].utils.clear_globalDevices_stream(deviceAddr);

							// init new stream
							parent.sensor_types[84].utils.init_globalDevices_stream(deviceAddr, payload, expected_packets, parsed);

							globalDevices[deviceAddr].last_packet_counter = current_packet;
							globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						}
					}
					else{
						// clear stream object & timeout
						parent.sensor_types[84].utils.clear_globalDevices_stream(deviceAddr);

						// init new stream
						parent.sensor_types[84].utils.init_globalDevices_stream(deviceAddr, payload, expected_packets, parsed);

						globalDevices[deviceAddr].last_packet_counter = current_packet;
						globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
					}
					if(current_packet == expected_packets){
						// concatenate stream
						sensor_data = parent.sensor_types[84].utils.concat_fft_data(deviceAddr, payload[8]);
						// clear stream object & timeout
						parent.sensor_types[84].utils.clear_globalDevices_stream(deviceAddr);
						return sensor_data;
					}
					else{
						return;
					}
				}
				else if(payload[8] === 0 || payload[8] === 2){
					// mode byte most significant bit will indicate fft data.
					// console.log(d);
					var odr;
					switch(payload[9]){
						case 6:
							odr = "50Hz"
							break;
						case 7:
							odr = "100Hz";
							break;
						case 8:
							odr = "200Hz";
							break;
						case 9:
							odr = "400Hz";
							break;
						case 10:
							odr = "800Hz";
							break;
						case 11:
							odr = "1600Hz";
							break;
						case 12:
							odr = "3200Hz";
							break;
						case 13:
							odr = "6400Hz";
							break;
						case 14:
							odr = "12800Hz";
							break;
						case 15:
							odr = "25600Hz";
							break;
					}
					return {
						mode: payload[8],

						odr: odr,
						temperature: signInt(payload.slice(10, 12).reduce(msbLsb), 16) / 100,

						x_rms_ACC_G: payload.slice(12, 14).reduce(msbLsb)/1000,
						x_max_ACC_G: payload.slice(14, 16).reduce(msbLsb)/1000,
						x_velocity_mm_sec: payload.slice(16, 18).reduce(msbLsb) / 100,
						x_displacement_mm: payload.slice(18, 20).reduce(msbLsb) / 100,
						x_peak_one_Hz: payload.slice(20, 22).reduce(msbLsb),
						x_peak_two_Hz: payload.slice(22, 24).reduce(msbLsb),
						x_peak_three_Hz: payload.slice(24, 26).reduce(msbLsb),

						y_rms_ACC_G: payload.slice(26, 28).reduce(msbLsb)/1000,
						y_max_ACC_G: payload.slice(28, 30).reduce(msbLsb)/1000,
						y_velocity_mm_sec: payload.slice(30, 32).reduce(msbLsb) / 100,
						y_displacement_mm: payload.slice(32, 34).reduce(msbLsb) / 100,
						y_peak_one_Hz: payload.slice(34, 36).reduce(msbLsb),
						y_peak_two_Hz: payload.slice(36, 38).reduce(msbLsb),
						y_peak_three_Hz: payload.slice(38, 40).reduce(msbLsb),

						z_rms_ACC_G: payload.slice(40, 42).reduce(msbLsb)/1000,
						z_max_ACC_G: payload.slice(42, 44).reduce(msbLsb)/1000,
						z_velocity_mm_sec: payload.slice(44, 46).reduce(msbLsb) / 100,
						z_displacement_mm: payload.slice(46, 48).reduce(msbLsb) / 100,
						z_peak_one_Hz: payload.slice(48, 50).reduce(msbLsb),
						z_peak_two_Hz: payload.slice(50, 52).reduce(msbLsb),
						z_peak_three_Hz: payload.slice(52, 54).reduce(msbLsb),
					};
				}
				// else{
				// 	parsed.data = {'error': 'Vibration mode error'}
				// 	return parsed;
				// }
			},
			'utils': {
				'clear_globalDevices_stream': (deviceAddr) => {
					if(Object.hasOwn(globalDevices, deviceAddr)){
						if(Object.hasOwn(globalDevices[deviceAddr], 'packet_stream_timeout')){
							clearTimeout(globalDevices[deviceAddr].packet_stream_timeout);
						}
						delete globalDevices[deviceAddr];
					}
				},
				'init_globalDevices_stream'(deviceAddr, payload, expected_packets, parsed){
					switch(payload[9]){
						case 6:
							odr = 50;
							break;
						case 7:
							odr = 100;
							break;
						case 8:
							odr = 200;
							break;
						case 9:
							odr = 400;
							break;
						case 10:
							odr = 800;
							break;
						case 11:
							odr = 1600;
							break;
						case 12:
							odr = 3200;
							break;
						case 13:
							odr = 6400;
							break;
						case 14:
							odr = 12800;
							break;
						case 15:
							odr = 25600;
							break;
						default:
							odr = 0;
					}
					globalDevices[deviceAddr] = {
						data: {},
						odr: odr,
						mo: payload[8],
						fsr: payload[10] >> 5,
						en_axis: payload[10] & 7,
						hour: payload[11],
						minute: payload[12],
						temperature: msbLsb(payload[13], payload[14]) / 100,
						expected_packets: expected_packets
					}
					globalDevices[deviceAddr].packet_stream_timeout = setTimeout(() => {
						parsed.sensor_data = parent.sensor_types[84].utils.concat_fft_data(deviceAddr, payload[8]);
						parsed.sensor_data.error = 'Time Series Data Stream Timeout - incomplete data received';

						parent._emitter.emit('sensor_data', parsed);
						parent._emitter.emit('sensor_data-84', parsed);
						parent._emitter.emit('sensor_data'+'-'+deviceAddr, parsed);
					}, 60000);
				},
				'concat_fft_data': (deviceAddr) => {
					var raw_data = new Array();
					for(const packet in globalDevices[deviceAddr].data){
						raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
					}
					var label = 0;
					var fft_concat = {};

					var en_axis_data = {};
					switch (globalDevices[deviceAddr].en_axis){
						case 1:
							en_axis_data.x_offset = 0;
							en_axis_data.increment = 2;
							break;
						case 2:
							en_axis_data.y_offset = 0;
							en_axis_data.increment = 2;
							break;
						case 3:
							en_axis_data.x_offset = 0;
							en_axis_data.y_offset = 2;
							en_axis_data.increment = 4;
							break;
						case 4:
							en_axis_data.z_offset = 0;
							en_axis_data.increment = 2;
							break;
						case 5:
							en_axis_data.x_offset = 0;
							en_axis_data.z_offset = 2;
							en_axis_data.increment = 4;
							break;
						case 6:
							en_axis_data.y_offset = 0;
							en_axis_data.z_offset = 2;
							en_axis_data.increment = 4;
							break;
						case 7:
							en_axis_data.x_offset = 0;
							en_axis_data.y_offset = 2;
							en_axis_data.z_offset = 4;
							en_axis_data.increment = 6;
							break;
						default:
							en_axis_data.increment = 0;
					}
					
					var fsr_mult = .00006;
					var fsr_text = "";

					switch(globalDevices[deviceAddr].fsr){
						case 0:
							fsr_mult = 0.00006;
							break;
						case 1:
							fsr_mult = 0.00012;
							break;
						case 2:
							fsr_mult = 0.00024;
							break;
						case 3:
							fsr_mult = 0.00049;
							break;
					}
					switch(globalDevices[deviceAddr].fsr){
						case 0:
							fsr_text = "2g";
							break;
						case 1:
							fsr_text = "4g";
							break;
						case 2:
							fsr_text = "8g";
							break;
						case 3:
							fsr_text = "16g";
							break;
					}

					for(var i = 0; i < raw_data.length; i+=en_axis_data.increment){
						label++;

						fft_concat[label] = {};

						if('x_offset' in en_axis_data){
							fft_concat[label].x = parseFloat((signInt(((raw_data[i+en_axis_data.x_offset]<<8)+(raw_data[i+en_axis_data.x_offset+1])), 16)*fsr_mult).toFixed(2));
						}
						if('y_offset' in en_axis_data){
							fft_concat[label].y = parseFloat((signInt(((raw_data[i+en_axis_data.y_offset]<<8)+(raw_data[i+en_axis_data.y_offset+1])), 16)*fsr_mult).toFixed(2));
						}
						if('z_offset' in en_axis_data){
							fft_concat[label].z = parseFloat((signInt(((raw_data[i+en_axis_data.z_offset]<<8)+(raw_data[i+en_axis_data.z_offset+1])), 16)*fsr_mult).toFixed(2));
						}
					}

					var fft_concat_obj = {
						time_id: [
							String(globalDevices[deviceAddr].hour).padStart(2, '0'),
							String(globalDevices[deviceAddr].minute).padStart(2, '0'),
						].join(':'),
						mac_address: deviceAddr,
						en_axis: globalDevices[deviceAddr].en_axis,
						fsr: fsr_text,
						odr: globalDevices[deviceAddr].odr,
						device_temp: globalDevices[deviceAddr].temperature,
						// total_samples: label,
						fft_confidence : ((Object.keys(globalDevices[deviceAddr].data).length / globalDevices[deviceAddr].expected_packets) * 100).toFixed(2) + '%',
						data: fft_concat
					};
					return fft_concat_obj;
				}
			},
			'parse_fly': (frame) => {
				let frame_data = {};
				switch(frame[16]){
					case 0:
						frame_data.mode = "Processed";
						break;
					case 1:
						frame_data.mode = "Raw";
						break;
					case 2:
						frame_data.mode = "Processed + Raw on demand";
						break;
				}
				switch(frame[17]){
					case 6:
						frame_data.odr_1 = 50;
						break;
					case 7:
						frame_data.odr_1 = 100;
						break;
					case 8:
						frame_data.odr_1 = 200;
						break;
					case 9:
						frame_data.odr_1 = 400;
						break;
					case 10:
						frame_data.odr_1 = 800;
						break;
					case 11:
						frame_data.odr_1 = 1600;
						break;
					case 12:
						frame_data.odr_1 = 3200;
						break;
					case 13:
						frame_data.odr_1 = 6400;
						break;
					case 14:
						frame_data.odr_1 = 12800;
						break;
					case 15:
						frame_data.odr_1 = 25600;
						break;
				}
				frame_data.sampling_duration_1 = frame[19]*50 + "ms";
				switch(frame[21]){
					case 0:
						frame_data.filter_status = "Disabled";
						break;
					case 1:
						frame_data.filter_status = "Enabled";
						break;
				}
				switch(frame[22]){
					case 0:
						frame_data.lpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.lpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.lpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.lpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.lpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.lpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.lpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.lpf_coeff_1 = 2048;
						break;
				}
				frame_data.lpf_freq_1 = frame_data.odr_1 / frame_data.lpf_coeff_1;
				switch(frame[24]){
					case 0:
						frame_data.hpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.hpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.hpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.hpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.hpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.hpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.hpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.hpf_coeff_1 = 2048;
						break;
				}
				frame_data.hpf_freq_1 = frame_data.odr_1 / frame_data.hpf_coeff_1;
				switch(frame[26]){
					case 0:
						frame_data.sampling_interval = "5 Minutes";
						break;
					case 1:
						frame_data.sampling_interval = "10 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "15 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "20 Minutes";
						break;
					case 4:
						frame_data.sampling_interval = "30 Minutes";
						break;
					case 5:
						frame_data.sampling_interval = "60 Minutes";
						break;
					case 6:
						frame_data.sampling_interval = "120 Minutes";
						break;
					case 7:
						frame_data.sampling_interval = "180 Minutes";
						break;
					case 8:
						frame_data.sampling_interval = "1 Minute";
						break;
				}
				frame_data.on_request_timeout = frame[27] + " Seconds";
				frame_data.deadband = frame[28] + "mg";

				switch(frame[29]){
					case 0:
						frame_data.payload_length = "50 Bytes";
						break;
					case 1:
						frame_data.payload_length = "100 Bytes";
						break;
					case 2:
						frame_data.payload_length = "150 Bytes";
						break;
					case 3:
						frame_data.payload_length = "180 Bytes";
						break;
				}

				let response = {
					'firmware': frame[2],
					'destination_address': toMac(frame.slice(12, 16)),
					'mode': frame_data.mode,
					'odr_1': frame_data.odr_1+'Hz',
					'sampling_duration_1': frame_data.sampling_duration_1,
					'filter_status': frame_data.filter_status,
					'lpf_coeff_1': frame_data.lpf_coeff_1,
					'lpf_freq_1': frame_data.lpf_freq_1+'Hz',
					'hpf_coeff_1': frame_data.hpf_coeff_1,
					'hpf_freq_1': frame_data.hpf_freq_1+'Hz',
					'sampling_interval': frame_data.sampling_interval,
					'on_request_timeout': frame_data.on_request_timeout,
					'deadband': frame_data.deadband,
					'payload_length': frame_data.payload_length
				}
				if(response.firmware > 19){
					response.lifetime_transmissions = frame.slice(30, 34).reduce(msbLsb);
				}
				response.machine_values = {
					'firmware': frame[2],
					'destination_address': toMac(frame.slice(12, 16), false),
					'mode': frame[16],
					'odr_1': frame[17],
					'sampling_duration_1': frame[19],
					'filter_status': frame[21],
					'lpf_coeff_1': frame[22],
					'hpf_coeff_1': frame[24],
					'sampling_interval': frame[26],
					'on_request_timeout': frame[27],
					'deadband': frame[28],
					'payload_length': frame[29]
				}
				if(response.firmware > 19){
					response.machine_values.lifetime_transmissions = frame.slice(30, 34);
				}
				return response;
			}
		},
		'90': {
			name: 'DC Current Sensor',
			parse: (d) => {
				return {
					raw_adc: d.slice(0, 2).reduce(msbLsb),
					amps: d.slice(2, 6).reduce(msbLsb) / 10,
				};
			},
			'parse_fly': (frame) => {
				let firmware = frame[2];
				if(firmware > 13){ // firmware 14 and above
					let frame_data = {};
					let auto_check_interval = frame.slice(20, 22).reduce(msbLsb);
					if(!auto_check_interval){
						frame_data.auto_check_interval = 'Disabled';
					}else{
						frame_data.auto_check_interval = auto_check_interval + " sec";
					}
					frame_data.always_on = frame[24] ? "Enabled" : "Disabled";
					switch(frame[16]){
						case 0:
							frame_data.fsr = "+-6.114 V";
						break;
						case 1:
							frame_data.fsr = "+-4.096 V";
						break;
						case 2:
							frame_data.fsr = "+-2.048 V";
						break;
						case 3:
							frame_data.fsr = "+-1.024 V";
						break;
						case 4:
							frame_data.fsr = "+-0.512 V";
						break;
						case 5:
							frame_data.fsr = "+-0.256 V";
						break;
					}
					return {
						'firmware': frame[2],
						'fsr': frame_data.fsr,
						'boot_up_time': frame[17] + " sec",
						'adc_pin_reading': frame.slice(18, 20).reduce(msbLsb),
						'auto_check_interval': frame_data.auto_check_interval,
						'auto_check_threshold': frame.slice(22, 24).reduce(msbLsb),
						'always_on': frame_data.always_on,
						'calibration_one': frame.slice(25, 29).reduce(msbLsb),
						'hardware_id': frame.slice(29, 32),
						'report_rate': frame.slice(32, 36).reduce(msbLsb) + " sec",
						'tx_life_counter': frame.slice(36, 40).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'fsr': frame[16],
							'boot_up_time': frame[17],
							'adc_pin_reading': frame.slice(18, 20),
							'auto_check_interval': frame.slice(20, 22),
							'auto_check_threshold': frame.slice(22, 24),
							'always_on': frame[24],
							'calibration_one': frame.slice(25, 29),
							'hardware_id': frame.slice(29, 32),
							'report_rate': frame.slice(32, 36),
							'tx_life_counter': frame.slice(36, 40)
						}
					}
				}
			}
		},
		'91': {
			name: 'Wireless Air Velocity Sensor HVAC',
			parse: (d) => {
				return {
					velocity_mps: signInt(msbLsb(d[0], d[1]), 16) / 1000
				};
			}
		},
		'92': {
			name: 'Sound Sensor',
			parse: (d) => {
				return {
					sound: signInt(d[3], 8)/100
				};
			}
		},
		'97': {
			name: 'One channel Dynamic Ultrasound vibration Sensor',
			parse: (payload, parsed, mac) => {
				if (payload[8] === 1) { // raw
					var mode = payload[8];
					var sampling_frequency = payload.slice(9, 13).reduce(msbLsb);
					var sensor_index = payload[13];
					var gain_db = payload[14];
					var deviceAddr = mac;
					var expected_packets = payload.slice(15, 17).reduce(msbLsb);
					var current_packet = payload.slice(17, 19).reduce(msbLsb);
					var sdata_start = 19;

					if(globalDevices.hasOwnProperty(deviceAddr) || expected_packets == 1){
						// if(expected_packets == 1){
						// 	this.build_102_data(payload, deviceAddr, hour, minute, sdata_start, current_packet, firmware);
						// }

						// if a packet is already stored with the same packet ID,
						// or if packet ID is 1,
						// or if current packet ID is not one more than last packet ID
						if(current_packet == 1 && expected_packets != 1) {
							if(current_packet in globalDevices[deviceAddr].data || !(((current_packet&127)-1) in globalDevices[deviceAddr].data)) {
								console.log('bad packet breakdown, deleting stream. Current packet:');
								console.log(current_packet);
								console.log('Total Expected Packets:');
								console.log(expected_packets);
								// console.log(current_packet in globalDevices[deviceAddr].data && current_packet == 1 && expected_packets != 1);
								// console.log(current_packet == 1);
								// console.log(!((current_packet-1) in globalDevices[deviceAddr].data));
								if(this.hasOwnProperty('failure_no')){
									this.failure_no = this.failure_no + 1;
								}
								else{
									this.failure_no = 1;
								}
								if(this.hasOwnProperty('failure_no')){
									console.log('####falure no');
									console.log(this.failure_no);
								}
								delete globalDevices[deviceAddr];
								if(current_packet != 1){
									return;
								} else{
									if(current_packet != 1){
										console.log('bad packet cleanup');
										return;
									}
									globalDevices[deviceAddr] = {
										// stream_size: expected_packets,
										data: {},
									}
									globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
									return;
								}
							}
						}
						if(expected_packets == 1){
							if(current_packet != 1){
								console.log('bad packet cleanup');
								return;
							}
							globalDevices[deviceAddr] = {
								// stream_size: expected_packets,
								data: {},
							}
							globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
							//return;
						} else{
							globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						}
						if(Object.keys(globalDevices[deviceAddr].data).length == expected_packets){
							var raw_data = new Array();
							for(const packet in globalDevices[deviceAddr].data){
								raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
							}
							var label = 0;
							// var fft = {
							// 	data: new Array()
							// 	// test: new Array()
							// };
							var fft = new Array();
							var adc = {};

							for(var i = 0; i < raw_data.length; i+=2){
								label++;
								adc[label] = (raw_data[i]<<8)+(raw_data[i+1]);
							}
							// var data = globalDevices[deviceAddr];
							delete globalDevices[deviceAddr];
							if(this.hasOwnProperty('failure_no')){
								console.log('####falure no');
								console.log(this.failure_no);
							}
							return{
								'mode': mode,
								'sampling_frequency': sampling_frequency,
								'sensor_index': sensor_index,
								'gain_db':gain_db,
								'adc':adc,
								'raw_data':raw_data
							}
						}
						else{
							return;
						}

					}else{
						if(current_packet != 1){
							console.log('bad packet cleanup');
							return;
						}
						globalDevices[deviceAddr] = {
							// stream_size: expected_packets,
							data: {},
						}
						globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						return;
					}
				}
				else if(payload[8] === 0){ // processed mode
					return {
						mode: payload[8],
						sampling_frequency: payload.slice(9, 13).reduce(msbLsb),
						rms_mv: signInt(payload.slice(13, 17).reduce(msbLsb), 32) / 1000,
						freq_1: payload.slice(17, 19).reduce(msbLsb),
						freq_2: payload.slice(19, 21).reduce(msbLsb),
						freq_3: payload.slice(21, 23).reduce(msbLsb),
					};
				}

			},
			'parse_fly': (frame) => {
				let frame_data = {};
				frame_data.mode = frame[12] ? 'Raw':'Processed';
				switch(frame[13]){
					case 0:
						frame_data.raw_lenght = 55;
					break;
					case 1:
						frame_data.raw_lenght = 100;
					break;
					case 2:
						frame_data.raw_lenght = 150;
					break;
					case 3:
						frame_data.raw_lenght = 180;
					break;
				}
				return {
					'firmware': frame[2],
					'mode': frame_data.mode,
					'raw_length': frame_data.raw_lenght + " Bytes",
					'raw_on_request_timeout': frame[14] + " sec",
					'fly_rate': frame.slice(15, 17).reduce(msbLsb) + " min",
					'sensor_gain_db': frame[17] + " dB",
					'sensor_boot_time': frame[18] + " sec",
					'sampling_frequency': frame.slice(19, 23).reduce(msbLsb) + " Hz",
					// 'hardware_id': frame.slice(23, 26),
					// 'report_rate': frame.slice(26, 30).reduce(msbLsb),
					// 'tx_life_count': frame.slice(30, 34).reduce(msbLsb),
					'machine_values': {
						'firmware': frame[2],
						'mode':frame[12],
						'raw_lenght': frame[13],
						'raw_on_request_timeout': frame[14],
						'fly_rate': frame.slice(15, 17),
						'sensor_gain_db': frame[17],
						'sensor_boot_time': frame[18],
						'sampling_frequency': frame.slice(19, 23)
						// 'hardware_id': frame.slice(23, 26),
						// 'report_rate': frame.slice(26, 30),
						// 'tx_life_count': frame.slice(30, 34)
					}
				}
			}
		},
		'98': {
			name: 'Two channel Dynamic Ultrasound vibration Sensor',
			parse: (payload, parsed, mac) => {
				if (payload[8] === 1) { // raw
					var mode = payload[8];
					var sampling_frequency = payload.slice(9, 13).reduce(msbLsb);
					var sensor_index = payload[13];
					var gain_db = payload[14];
					var deviceAddr = mac;
					var expected_packets = payload.slice(15, 17).reduce(msbLsb);
					var current_packet = payload.slice(17, 19).reduce(msbLsb);
					var sdata_start = 19;

					if(globalDevices.hasOwnProperty(deviceAddr) || expected_packets == 1){
						// if(expected_packets == 1){
						// 	this.build_102_data(payload, deviceAddr, hour, minute, sdata_start, current_packet, firmware);
						// }

						// if a packet is already stored with the same packet ID,
						// or if packet ID is 1,
						// or if current packet ID is not one more than last packet ID
						if(current_packet == 1 && expected_packets != 1) {
							if(current_packet in globalDevices[deviceAddr].data || !(((current_packet&127)-1) in globalDevices[deviceAddr].data)) {
								console.log('bad packet breakdown, deleting stream. Current packet:');
								console.log(current_packet);
								console.log('Total Expected Packets:');
								console.log(expected_packets);
								// console.log(current_packet in globalDevices[deviceAddr].data && current_packet == 1 && expected_packets != 1);
								// console.log(current_packet == 1);
								// console.log(!((current_packet-1) in globalDevices[deviceAddr].data));
								if(this.hasOwnProperty('failure_no')){
									this.failure_no = this.failure_no + 1;
								}
								else{
									this.failure_no = 1;
								}
								if(this.hasOwnProperty('failure_no')){
									console.log('####falure no');
									console.log(this.failure_no);
								}
								delete globalDevices[deviceAddr];
								if(current_packet != 1){
									return;
								} else{
									if(current_packet != 1){
										console.log('bad packet cleanup');
										return;
									}
									globalDevices[deviceAddr] = {
										// stream_size: expected_packets,
										data: {},
									}
									globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
									return;
								}
							}
						}
						if(expected_packets == 1){
							if(current_packet != 1){
								console.log('bad packet cleanup');
								return;
							}
							globalDevices[deviceAddr] = {
								// stream_size: expected_packets,
								data: {},
							}
							globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
							//return;
						} else{
							globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						}
						if(Object.keys(globalDevices[deviceAddr].data).length == expected_packets){
							var raw_data = new Array();
							for(const packet in globalDevices[deviceAddr].data){
								raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
							}
							var label = 0;
							// var fft = {
							// 	data: new Array()
							// 	// test: new Array()
							// };
							var fft = new Array();
							var adc = {};

							for(var i = 0; i < raw_data.length; i+=2){
								label++;
								adc[label] = (raw_data[i]<<8)+(raw_data[i+1]);
							}
							// var data = globalDevices[deviceAddr];
							delete globalDevices[deviceAddr];
							if(this.hasOwnProperty('failure_no')){
								console.log('####falure no');
								console.log(this.failure_no);
							}
							return{
								'mode': mode,
								'sampling_frequency': sampling_frequency,
								'sensor_index': sensor_index,
								'gain_db':gain_db,
								'adc':adc,
								'raw_data':raw_data
							}
						}
						else{
							return;
						}

					}else{
						if(current_packet != 1){
							console.log('bad packet cleanup');
							return;
						}
						globalDevices[deviceAddr] = {
							// stream_size: expected_packets,
							data: {},
						}
						globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						return;
					}
				}
				else if(payload[8] === 0){ // processed mode
					return {
						mode: payload[8],
						sampling_frequency: payload.slice(9, 13).reduce(msbLsb),
						c1_rms_mv: signInt(payload.slice(13, 17).reduce(msbLsb), 32) / 1000,
						c1_freq_1: payload.slice(17, 19).reduce(msbLsb),
						c1_freq_2: payload.slice(19, 21).reduce(msbLsb),
						c1_freq_3: payload.slice(21, 23).reduce(msbLsb),
						c2_rms_mv: signInt(payload.slice(23, 27).reduce(msbLsb), 32) / 1000,
						c2_freq_1: payload.slice(27, 29).reduce(msbLsb),
						c2_freq_2: payload.slice(29, 31).reduce(msbLsb),
						c2_freq_3: payload.slice(31, 33).reduce(msbLsb)
					};
				}
			},
			'parse_fly': (frame) => {
				let firmware = frame[2];
				if(firmware > 13){ // firmware 14 and above
					let frame_data = {};
					let auto_check_interval = frame.slice(20, 22).reduce(msbLsb);
					if(!auto_check_interval){
						frame_data.auto_check_interval = 'Disabled';
					}else{
						frame_data.auto_check_interval = auto_check_interval + " sec";
					}
					frame_data.always_on = frame[24]?"Enabled":"Disabled";
					switch(frame[16]){
						case 0:
							frame_data.fsr = "+-6.114 V";
						break;
						case 1:
							frame_data.fsr = "+-4.096 V";
						break;
						case 2:
							frame_data.fsr = "+-2.048 V";
						break;
						case 3:
							frame_data.fsr = "+-1.024 V";
						break;
						case 4:
							frame_data.fsr = "+-0.512 V";
						break;
						case 5:
							frame_data.fsr = "+-0.256 V";
						break;
					}
					return {
						'firmware': frame[2],
						'fsr': frame_data.fsr,
						'boot_up_time': frame[17] + " sec",
						'adc_pin_reading': frame.slice(18, 20).reduce(msbLsb),
						'auto_check_interval': frame_data.auto_check_interval,
						'auto_check_threshold': frame.slice(22, 24).reduce(msbLsb),
						'always_on': frame_data.always_on,
						'calibration_one': frame.slice(25, 29).reduce(msbLsb),
						'calibration_two':frame.slice(29, 33).reduce(msbLsb),
						'calibration_three':frame.slice(33, 37).reduce(msbLsb),
						'hardware_id': frame.slice(37, 40),
						'report_rate': frame.slice(40, 44).reduce(msbLsb) + " sec",
						'tx_life_counter': frame.slice(44, 48).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'fsr': frame[16],
							'boot_up_time': frame[17],
							'adc_pin_reading': frame.slice(18, 20),
							'auto_check_interval': frame.slice(20, 22),
							'auto_check_percentage': frame.slice(22, 24),
							'always_on': frame[24],
							'calibration_one': frame.slice(25, 29),
							'calibration_two':frame.slice(29, 33),
							'calibration_three':frame.slice(33, 37),
							'hardware_id': frame.slice(37, 40),
							'report_rate': frame.slice(40, 44),
							'tx_life_counter': frame.slice(44, 48)
						}
					}
				}
				else{
					let frame_data = {};
					frame_data.mode = frame[12] ? 'Raw':'Processed';
					switch(frame[13]){
						case 0:
							frame_data.raw_lenght = 55;
						break;
						case 1:
							frame_data.raw_lenght = 100;
						break;
						case 2:
							frame_data.raw_lenght = 150;
						break;
						case 3:
							frame_data.raw_lenght = 180;
						break;
					}
					return {
						'firmware': frame[2],
						'mode': frame_data.mode,
						'raw_length': frame_data.raw_lenght + " Bytes",
						'raw_on_request_timeout': frame[14] + " sec",
						'fly_rate': frame.slice(15, 17).reduce(msbLsb) + " min",
						'c1_sensor_gain_db': frame[17] + " dB",
						'c1_sensor_boot_time': frame[18] + " sec",
						'c2_sensor_gain_db': frame[19] + " dB",
						'c2_sensor_boot_time': frame[20] + " sec",
						'sampling_frequency': frame.slice(21, 25).reduce(msbLsb) + " Hz",
						// 'hardware_id': frame.slice(25, 28),
						// 'report_rate': frame.slice(28, 32).reduce(msbLsb),
						// 'tx_life_count': frame.slice(32, 36).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'mode': frame[12],
							'raw_lenght': frame[13],
							'raw_on_request_timeout': frame[14],
							'fly_rate': frame.slice(15, 17),
							'c1_sensor_gain_db': frame[17],
							'c1_sensor_boot_time': frame[18],
							'c2_sensor_gain_db': frame[19],
							'c2_sensor_boot_time': frame[20],
							'sampling_frequency': frame.slice(21, 25)
							// 'hardware_id': frame.slice(25, 28),
							// 'report_rate': frame.slice(28, 32),
							// 'tx_life_count': frame.slice(32, 36)
						}
					}
				}
			}
		},
		'99': {
			name: 'Laser Sensor',
			parse: (d) => {
				return {
					distance: d.slice(0, 2).reduce(msbLsb)
				};
			},
			'parse_fly': (frame) => {
				return {
					'firmware': frame[2],
					'hardware_id': frame.slice(12, 15),
					'report_rate': frame.slice(15, 19).reduce(msbLsb) + " sec",
					'tx_life_counter': frame.slice(19, 23).reduce(msbLsb),
					'machine_values': {
						'firmware': frame[2],
						'hardware_id': frame.slice(12, 15),
						'report_rate': frame.slice(15, 19),
						'tx_life_counter': frame.slice(19, 23)
					}
				}
			}
		},
		'101':{
			name: 'Pro Vibration',
			parse: (d, full)=>{
				return{
					mode: full[7] == 1? "raw" : "normal"
				};
			}
		},
		'108': {
			name: 'Machine Uptime Monitoring Sensor',
			parse: (d, payload) => {
				let firmware = payload[1];
				// if((payload[7] & 2) != 0){
				// 	console.log('Error found');
				// 	// parsed.data = {error: 'Error found, Accelerometer Probe may be unattached'};
				// 	let error = {error: 'Error found, Accelerometer Probe may be unattached'};
				// 	return error;
				// }
				if(firmware > 4){
					let report_type = "Regular";
					switch(d[41]){
						case 0:
							report_type = "Regular";
							break;
						case 1:
							report_type = "Shift end";
							break;
						case 2:
							report_type = "Interrupt";
							break;
						case 3:
							report_type = "Threshold";
							break;
					}
					let res = {
						digital_input_counter: d.slice(0, 4).reduce(msbLsb),
						digital_input_uptime: d.slice(4, 8).reduce(msbLsb),
						ct_input_counter: d.slice(8, 12).reduce(msbLsb),
						ct_input_uptime: d.slice(12, 16).reduce(msbLsb),
						opto_input_counter: d.slice(16, 20).reduce(msbLsb),
						opto_input_uptime: d.slice(20, 24).reduce(msbLsb),
						accelerometer_counter: d.slice(24, 28).reduce(msbLsb),
						accelerometer_uptime: d.slice(28, 32).reduce(msbLsb),
						magnetometer_counter: d.slice(32, 36).reduce(msbLsb),
						magnetometer_uptime: d.slice(36, 40).reduce(msbLsb),
						input_di: d[40] & 1 ? 1 : 0,
						input_ct: d[40] & 2 ? 1 : 0,
						input_opto: d[40] & 4 ? 1 : 0,
						input_acc: d[40] & 8 ? 1 : 0,
						input_mag: d[40] & 16 ? 1 : 0,
						report_type: report_type,
						rtc: [
							String(d[42]).padStart(2, '0'),
							String(d[43]).padStart(2, '0'),
							String(d[44]).padStart(2, '0')
						].join(':')
					};
					if((payload[7] & 2) != 0){
						res.error = {
							accelerometer_counter: "Error found, Accelerometer Probe may be unattached",
							accelerometer_uptime: "Error found, Accelerometer Probe may be unattached",
							input_acc: "Error found, Accelerometer Probe may be unattached"
						}
						// res.accelerometer_counter = "No probe detected";
						// res.accelerometer_uptime = "No probe detected";
						// res.input_acc = "No probe detected";
					};
					return res;
				}else{
					let res = {
						digital_input_counter: d.slice(0, 4).reduce(msbLsb),
						digital_input_uptime: d.slice(4, 8).reduce(msbLsb),
						ct_input_counter: d.slice(8, 12).reduce(msbLsb),
						ct_input_uptime: d.slice(12, 16).reduce(msbLsb),
						opto_input_counter: d.slice(16, 20).reduce(msbLsb),
						opto_input_uptime: d.slice(20, 24).reduce(msbLsb),
						accelerometer_counter: d.slice(24, 28).reduce(msbLsb),
						accelerometer_uptime: d.slice(28, 32).reduce(msbLsb),
						magnetometer_counter: d.slice(32, 36).reduce(msbLsb),
						magnetometer_uptime: d.slice(36, 40).reduce(msbLsb),
						input_di: d[40] & 1 ? 1 : 0,
						input_ct: d[40] & 2 ? 1 : 0,
						input_opto: d[40] & 4 ? 1 : 0,
						input_acc: d[40] & 8 ? 1 : 0,
						input_mag: d[40] & 16 ? 1 : 0
					};
					if((payload[7] & 2) != 0){
						res.error = {
							accelerometer_counter: "Error found, Accelerometer Probe may be unattached",
							accelerometer_uptime: "Error found, Accelerometer Probe may be unattached",
							input_acc: "Error found, Accelerometer Probe may be unattached"
						}
					};
					return res;
				}
			},
			'parse_fly': (frame) => {
				if(frame[2] > 18){
					let reset_mode = "Disabled";
					switch(frame[38]){
						case 0:
							reset_mode = "Disabled";
							break;
						case 1:
							reset_mode = "Shift Ends";
							break;
						case 2:
							reset_mode = "Timeout";
							break;
					}
					let acc_odr = "10 Hz";
					switch(frame[40]){
						case 0:
							acc_odr = "10 Hz";
							break;
						case 1:
							acc_odr = "20 Hz";
							break;
						case 2:
							acc_odr = "50 Hz";
							break;
						case 3:
							acc_odr = "100 Hz";
							break;
						case 4:
							acc_odr = "200 Hz";
							break;
						case 5:
							acc_odr = "400 Hz";
							break;
					}
					let rtc_sampling_interval = "5 sec";
					switch(frame[39]){
						case 0:
							rtc_sampling_interval = "1 min";
							break;
						case 1:
							rtc_sampling_interval = "5 min";
							break;
						case 2:
							rtc_sampling_interval = "15 min";
							break;
						case 3:
							rtc_sampling_interval = "30 min";
							break;
						case 4:
							rtc_sampling_interval = "1 hour";
							break;
						case 5:
							rtc_sampling_interval = "2 hours";
							break;
						case 6:
							rtc_sampling_interval = "3 hours";
							break;
						case 7:
							rtc_sampling_interval = "6 hours";
							break;
						case 8:
							rtc_sampling_interval = "12 hours";
							break;
						case 9:
							rtc_sampling_interval = "5 sec";
							break;
						case 10:
							rtc_sampling_interval = "10 sec";
							break;
						case 11:
							rtc_sampling_interval = "15 sec";
							break;
						case 12:
							rtc_sampling_interval = "30 sec";
							break;
					}
					let screen_control = 0;
					switch(frame[41]){
						case 0:
							screen_control = "IO1";
							break;
						case 1:
							screen_control = "IO2";
							break;
						case 2:
							screen_control = "IO3";
							break;
						case 3:
							screen_control = "Accelero";
							break;
						case 4:
							screen_control = "Magneto";
							break;
					}
					return {
						'firmware': frame[2],
						'accelerometer_threshold': (frame[16]* 32) + " mg",
						'debouncing_timeout': frame.slice(17, 19).reduce(msbLsb) + " msec",
						'accelero_state': frame[19]? "Enabled": "Disabled",
						'input_1_active_edge': frame[20]? "Rising": "Falling",
						'input_2_active_edge': frame[21]? "Rising": "Falling",
						'input_3_active_edge': frame[22]? "Rising": "Falling",
						'counter_threshold': frame.slice(23, 27).reduce(msbLsb),
						'trasnmit_on_change_status': frame[27]? "Enabled": "Disabled",
						'Shift_end_1': [
							String(frame[28]).padStart(2, '0'),
							String(frame[29]).padStart(2, '0')
						].join(':'),
						'Shift_end_2': [
							String(frame[30]).padStart(2, '0'),
							String(frame[31]).padStart(2, '0')
						].join(':'),
						'Shift_end_3': [
							String(frame[32]).padStart(2, '0'),
							String(frame[33]).padStart(2, '0')
						].join(':'),
						'Shift_end_4': [
							String(frame[34]).padStart(2, '0'),
							String(frame[35]).padStart(2, '0')
						].join(':'),
						'reset_timeout': frame.slice(36, 38).reduce(msbLsb) + " min",
						'counter_reset_mode': reset_mode,
						'sampling_interval': rtc_sampling_interval,
						'acc_odr': acc_odr,
						'screen_control': screen_control,
						'screen_on_time': frame[42] + ' sec',
						'interrupt_timeout': frame.slice(43, 45).reduce(msbLsb) + ' msec',
						'hardware_id': frame.slice(45, 48),
						'report_rate': frame.slice(48, 52).reduce(msbLsb) + " sec",
						'tx_life_counter': frame.slice(52, 56).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'accelerometer_threshold': frame[16],
							'debouncing_timeout': frame.slice(17, 19),
							'accelero_state': frame[19],
							'input_1_active_edge': frame[20],
							'input_2_active_edge': frame[21],
							'input_3_active_edge': frame[22],
							'counter_threshold': frame.slice(23, 27),
							'trasnmit_on_change_status': frame[27],
							'Shift_end_1': frame.slice(28, 30),
							'Shift_end_2': frame.slice(30, 32),
							'Shift_end_3': frame.slice(32, 34),
							'Shift_end_4': frame.slice(34, 36),
							'reset_timeout': frame.slice(36, 38),
							'counter_reset_mode': frame[38],
							'sampling_interval': frame[39],
							'acc_odr': frame[40],
							'screen_control': frame[41],
							'screen_on_time': frame[42],
							'interrupt_timeout': frame.slice(43, 45),
							'hardware_id': frame.slice(45, 48),
							'report_rate': frame.slice(48, 52),
							'tx_life_counter': frame.slice(52, 56)
						}
					}
				}else if(frame[2] > 13){
					let reset_mode = "Disabled";
					switch(frame[38]){
						case 0:
							reset_mode = "Disabled";
							break;
						case 1:
							reset_mode = "Shift Ends";
							break;
						case 2:
							reset_mode = "Timeout";
							break;
					}
					let acc_odr = "10Hz";
					switch(frame[40]){
						case 0:
							acc_odr = "10Hz";
							break;
						case 1:
							acc_odr = "20Hz";
							break;
						case 2:
							acc_odr = "50Hz";
							break;
						case 3:
							acc_odr = "100Hz";
							break;
						case 4:
							acc_odr = "200Hz";
							break;
						case 5:
							acc_odr = "400Hz";
							break;
					}
					let rtc_sampling_interval = "5 seconds";
					switch(frame[39]){
						case 0:
							rtc_sampling_interval = "1 minute";
							break;
						case 1:
							rtc_sampling_interval = "5 minutes";
							break;
						case 2:
							rtc_sampling_interval = "15 minutes";
							break;
						case 3:
							rtc_sampling_interval = "30 minutes";
							break;
						case 4:
							rtc_sampling_interval = "1 hour";
							break;
						case 5:
							rtc_sampling_interval = "2 hours";
							break;
						case 6:
							rtc_sampling_interval = "3 hours";
							break;
						case 7:
							rtc_sampling_interval = "6 hours";
							break;
						case 8:
							rtc_sampling_interval = "12 hours";
							break;
						case 9:
							rtc_sampling_interval = "5 seconds";
							break;
						case 10:
							rtc_sampling_interval = "10 seconds";
							break;
						case 11:
							rtc_sampling_interval = "15 seconds";
							break;
						case 12:
							rtc_sampling_interval = "30 seconds";
							break;
					}
					return {
						'firmware': frame[2],
						'accelerometer_threshold': (frame[16]* 32) + "mg",
						'debouncing_timeout': frame.slice(17, 19).reduce(msbLsb) + "msec",
						'accelero_state': frame[19]? "Enabled": "Disabled",
						'input_1_active_edge': frame[20]? "Rising": "Falling",
						'input_2_active_edge': frame[21]? "Rising": "Falling",
						'input_3_active_edge': frame[22]? "Rising": "Falling",
						'counter_threshold': frame.slice(23, 27).reduce(msbLsb),
						'trasnmit_on_change_status': frame[27]? "Enabled": "Disabled",
						'Shift_end_1': [
							String(frame[28]).padStart(2, '0'),
							String(frame[29]).padStart(2, '0')
						].join(':'),
						'Shift_end_2': [
							String(frame[30]).padStart(2, '0'),
							String(frame[31]).padStart(2, '0')
						].join(':'),
						'Shift_end_3': [
							String(frame[32]).padStart(2, '0'),
							String(frame[33]).padStart(2, '0')
						].join(':'),
						'Shift_end_4': [
							String(frame[34]).padStart(2, '0'),
							String(frame[35]).padStart(2, '0')
						].join(':'),
						'reset_timeout': frame.slice(36, 38).reduce(msbLsb) + "min",
						'counter_reset_mode': reset_mode,
						'sampling_interval': rtc_sampling_interval,
						'acc_odr': acc_odr,
						'counter_ignore_timeout': frame.slice(41, 43).reduce(msbLsb) + "sec",
						'hardware_id': frame.slice(43, 46),
						'report_rate': frame.slice(46, 50).reduce(msbLsb) + "sec",
						'tx_life_counter': frame.slice(50, 54).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'accelerometer_threshold': frame[16],
							'debouncing_timeout': frame.slice(17, 19),
							'accelero_state': frame[19],
							'input_1_active_edge': frame[20],
							'input_2_active_edge': frame[21],
							'input_3_active_edge': frame[22],
							'counter_threshold': frame.slice(23, 27),
							'trasnmit_on_change_status': frame[27],
							'Shift_end_1': frame.slice(28, 30),
							'Shift_end_2': frame.slice(30, 32),
							'Shift_end_3': frame.slice(32, 34),
							'Shift_end_4': frame.slice(34, 36),
							'reset_timeout': frame.slice(36, 38),
							'counter_reset_mode': frame[38],
							'sampling_interval': frame[39],
							'acc_odr': frame[40],
							'counter_ignore_timeout': frame.slice(41, 43),
							'hardware_id': frame.slice(43, 46),
							'report_rate': frame.slice(46, 50),
							'tx_life_counter': frame.slice(50, 54)
						}
					}
				} else if(frame[2] > 9){
					let reset_mode = "Disabled";
					switch(frame[38]){
						case 0:
							reset_mode = "Disabled";
							break;
						case 1:
							reset_mode = "Shift Ends";
							break;
						case 2:
							reset_mode = "Timeout";
							break;
					}
					let acc_odr = "10Hz";
					switch(frame[40]){
						case 0:
							acc_odr = "10Hz";
							break;
						case 1:
							acc_odr = "20Hz";
							break;
						case 2:
							acc_odr = "50Hz";
							break;
						case 3:
							acc_odr = "100Hz";
							break;
						case 4:
							acc_odr = "200Hz";
							break;
						case 5:
							acc_odr = "400Hz";
							break;
					}
					let rtc_sampling_interval = "5 seconds";
					switch(frame[39]){
						case 0:
							rtc_sampling_interval = "1 minute";
							break;
						case 1:
							rtc_sampling_interval = "5 minutes";
							break;
						case 2:
							rtc_sampling_interval = "15 minutes";
							break;
						case 3:
							rtc_sampling_interval = "30 minutes";
							break;
						case 4:
							rtc_sampling_interval = "1 hour";
							break;
						case 5:
							rtc_sampling_interval = "2 hours";
							break;
						case 6:
							rtc_sampling_interval = "3 hours";
							break;
						case 7:
							rtc_sampling_interval = "6 hours";
							break;
						case 8:
							rtc_sampling_interval = "12 hours";
							break;
						case 9:
							rtc_sampling_interval = "5 seconds";
							break;
						case 10:
							rtc_sampling_interval = "10 seconds";
							break;
						case 11:
							rtc_sampling_interval = "15 seconds";
							break;
						case 12:
							rtc_sampling_interval = "30 seconds";
							break;
					}
					return {
						'firmware': frame[2],
						'accelerometer_threshold': (frame[16]* 32) + "mg",
						'debouncing_timeout': frame.slice(17, 19).reduce(msbLsb) + "msec",
						'accelero_state': frame[19]? "Enabled": "Disabled",
						'input_1_active_edge': frame[20]? "Rising": "Falling",
						'input_2_active_edge': frame[21]? "Rising": "Falling",
						'input_3_active_edge': frame[22]? "Rising": "Falling",
						'counter_threshold': frame.slice(23, 27).reduce(msbLsb),
						'trasnmit_on_change_status': frame[27]? "Enabled": "Disabled",
						'Shift_end_1': [
							String(frame[28]).padStart(2, '0'),
							String(frame[29]).padStart(2, '0')
						].join(':'),
						'Shift_end_2': [
							String(frame[30]).padStart(2, '0'),
							String(frame[31]).padStart(2, '0')
						].join(':'),
						'Shift_end_3': [
							String(frame[32]).padStart(2, '0'),
							String(frame[33]).padStart(2, '0')
						].join(':'),
						'Shift_end_4': [
							String(frame[34]).padStart(2, '0'),
							String(frame[35]).padStart(2, '0')
						].join(':'),
						'reset_timeout': frame.slice(36, 38).reduce(msbLsb) + "min",
						'counter_reset_mode': reset_mode,
						'sampling_interval': rtc_sampling_interval,
						'acc_odr': acc_odr,
						'hardware_id': frame.slice(41, 44),
						'report_rate': frame.slice(44, 48).reduce(msbLsb) + "sec",
						'tx_life_counter': frame.slice(48, 52).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'accelerometer_threshold': frame[16],
							'debouncing_timeout': frame.slice(17, 19),
							'accelero_state': frame[19],
							'input_1_active_edge': frame[20],
							'input_2_active_edge': frame[21],
							'input_3_active_edge': frame[22],
							'counter_threshold': frame.slice(23, 27),
							'trasnmit_on_change_status': frame[27],
							'Shift_end_1': frame.slice(28, 30),
							'Shift_end_2': frame.slice(30, 32),
							'Shift_end_3': frame.slice(32, 34),
							'Shift_end_4': frame.slice(34, 36),
							'reset_timeout': frame.slice(36, 38),
							'counter_reset_mode': frame[38],
							'sampling_interval': frame[39],
							'acc_odr': frame[40],
							'hardware_id': frame.slice(41, 44),
							'report_rate': frame.slice(44, 48),
							'tx_life_counter': frame.slice(48, 52)
						}
					}
				} else if(frame[2] > 8){
					let reset_mode = "Disabled";
					switch(frame[37]){
						case 0:
							reset_mode = "Disabled";
							break;
						case 1:
							reset_mode = "Shift Ends";
							break;
						case 2:
							reset_mode = "Timeout";
							break;
					}
					let acc_odr = "10Hz";
					switch(frame[39]){
						case 0:
							acc_odr = "10Hz";
							break;
						case 1:
							acc_odr = "20Hz";
							break;
						case 2:
							acc_odr = "50Hz";
							break;
						case 3:
							acc_odr = "100Hz";
							break;
						case 4:
							acc_odr = "200Hz";
							break;
						case 5:
							acc_odr = "400Hz";
							break;
					}
					let rtc_sampling_interval = "5 seconds";
					switch(frame[38]){
						case 0:
							rtc_sampling_interval = "1 minute";
							break;
						case 1:
							rtc_sampling_interval = "5 minutes";
							break;
						case 2:
							rtc_sampling_interval = "15 minutes";
							break;
						case 3:
							rtc_sampling_interval = "30 minutes";
							break;
						case 4:
							rtc_sampling_interval = "1 hour";
							break;
						case 5:
							rtc_sampling_interval = "2 hours";
							break;
						case 6:
							rtc_sampling_interval = "3 hours";
							break;
						case 7:
							rtc_sampling_interval = "6 hours";
							break;
						case 8:
							rtc_sampling_interval = "12 hours";
							break;
						case 9:
							rtc_sampling_interval = "5 seconds";
							break;
						case 10:
							rtc_sampling_interval = "10 seconds";
							break;
						case 11:
							rtc_sampling_interval = "15 seconds";
							break;
						case 12:
							rtc_sampling_interval = "30 seconds";
							break;
					}
					return {
						'firmware': frame[2],
						'accelerometer_threshold': (frame[16]* 32) + "mg",
						'debouncing_timeout': frame[17] + "msec",
						'accelero_state': frame[18]? "Enabled": "Disabled",
						'input_1_active_edge': frame[19]? "Rising": "Falling",
						'input_2_active_edge': frame[20]? "Rising": "Falling",
						'input_3_active_edge': frame[21]? "Rising": "Falling",
						'counter_threshold': frame.slice(22, 26).reduce(msbLsb),
						'trasnmit_on_change_status': frame[26]? "Enabled": "Disabled",
						'Shift_end_1': [
							String(frame[27]).padStart(2, '0'),
							String(frame[28]).padStart(2, '0')
						].join(':'),
						'Shift_end_2': [
							String(frame[29]).padStart(2, '0'),
							String(frame[30]).padStart(2, '0')
						].join(':'),
						'Shift_end_3': [
							String(frame[31]).padStart(2, '0'),
							String(frame[32]).padStart(2, '0')
						].join(':'),
						'Shift_end_4': [
							String(frame[33]).padStart(2, '0'),
							String(frame[34]).padStart(2, '0')
						].join(':'),
						'reset_timeout': frame.slice(35, 37).reduce(msbLsb) + "min",
						'counter_reset_mode': reset_mode,
						'sampling_interval': rtc_sampling_interval,
						'acc_odr': acc_odr,
						'hardware_id': frame.slice(40, 43),
						'report_rate': frame.slice(43, 47).reduce(msbLsb) + "sec",
						'tx_life_counter': frame.slice(47, 51).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'accelerometer_threshold': frame[16],
							'debouncing_timeout': frame[17],
							'accelero_state': frame[18],
							'input_1_active_edge': frame[19],
							'input_2_active_edge': frame[20],
							'input_3_active_edge': frame[21],
							'counter_threshold': frame.slice(22, 26),
							'trasnmit_on_change_status': frame[26],
							'Shift_end_1': frame.slice(27, 29),
							'Shift_end_2': frame.slice(29, 31),
							'Shift_end_3': frame.slice(31, 33),
							'Shift_end_4': frame.slice(33, 35),
							'reset_timeout': frame.slice(35, 37),
							'counter_reset_mode': frame[37],
							'sampling_interval': frame[38],
							'acc_odr': frame[39],
							'hardware_id': frame.slice(40, 43),
							'report_rate': frame.slice(43, 47),
							'tx_life_counter': frame.slice(47, 51)
						}
					}
				} else{
					return {
						'firmware': frame[2],
						'report_rate': frame.slice(12, 16).reduce(msbLsb).toString() + "sec.",
						'accelerometer_threshold': (frame[16]* 32) + "mg.",
						'debouncing_timeout': frame[17].toString() + "msec.",
						'accelero_state': frame[18],
						'digital_inputs_active_edge': frame.slice(19, 22).reduce(msbLsb),
						'counter_threshold': frame.slice(22, 26).reduce(msbLsb),
						'trasnmit_on_change_status': frame[26],
						'machine_values': {
							'firmware': frame[2],
							'report_rate': frame.slice(12, 16),
							'accelerometer_threshold': frame[16],
							'debouncing_timeout': frame[17],
							'accelero_active_state': frame[18],
							'digital_inputs_active_edge': frame.slice(19, 22),
							'counter_threshold': frame.slice(22, 26),
							'trasnmit_on_change_status': frame[26]
						}
					}
				}
			}
		},
		'109': {
			name: 'Wireless Custom Solar Sensor',
			parse: (d) => {
				return {
					illuminance: d.slice(0, 4).reduce(msbLsb),
					total_solar_radiation: d.slice(4, 6).reduce(msbLsb),
					ultraviolet_radiation: d.slice(6, 8).reduce(msbLsb)
				};
			},
			'parse_fly': (frame) => {
				return {
					'firmware': frame[2],
					'hardware_id': frame.slice(12, 15),
					'sample_rate': frame.slice(15, 19).reduce(msbLsb) + " Sec.",
					'tx_life_counter': frame.slice(19, 23).reduce(msbLsb),
					'machine_values': {
						'firmware': frame[2],
						'hardware_id': frame.slice(12, 15),
						'sample_rate': frame.slice(15, 19).reduce(msbLsb),
						'tx_life_counter': frame.slice(19, 23).reduce(msbLsb)
					}
				}
			}
		},
		'117': {
			name: 'Custom Vibration Sensor PPV',
			parse: (payload, parsed, mac) => {
				if(payload[7] & 2){
					console.log('Error found');
					parsed.data = {error: 'Error found, Sensor Probe may be unattached'};
					return parsed;
				}
				if(payload[8] === 1){
					let mode = payload[8];
					let msg_type = '';
					switch(payload[9]){
						case 0:
							msg_type = 'Regular';
							break;
						case 1:
							msg_type = 'Cautionay';
							break;
						case 2:
							msg_type = 'Artwork';
							break;
						case 3:
							msg_type = 'Transient';
							break;
					}
					var deviceAddr = mac;
					var firmware = payload[1];
					var odr = msbLsb(payload[10], payload[11]);
					var fsr = payload[12];
					var temperature = msbLsb(payload[13], payload[14])/100;
					var expected_packets = msbLsb(payload[15], payload[16]);
					var current_packet = msbLsb(payload[17], payload[18]);
					var sdata_start = 19;

					if(globalDevices.hasOwnProperty(deviceAddr) || expected_packets == 1){
						if(expected_packets != 1){
							// if a packet is already stored with the same packet ID, or if packet ID is 1, or if current packet ID is not one more than last packet ID
							if(current_packet in globalDevices[deviceAddr].data || current_packet == 1 || !(((current_packet)-1) in globalDevices[deviceAddr].data)) {
								console.log('-----');
								console.log('bad packet breakdown deleting stream');
								console.log(current_packet);
								console.log(expected_packets);
								console.log(current_packet in globalDevices[deviceAddr].data);
								console.log(current_packet == 1);
								console.log(!((current_packet-1) in globalDevices[deviceAddr].data));
								if(this.hasOwnProperty('failure_no')){
									this.failure_no = this.failure_no + 1;
								}
								else{
									this.failure_no = 1;
								}
								if(this.hasOwnProperty('failure_no')){
									console.log('####falure no');
									console.log(this.failure_no);
								}
								// console.log(globalDevices[deviceAddr].data);
								delete globalDevices[deviceAddr];
								if(current_packet != 1){
									return;
								} else{

									globalDevices[deviceAddr] = {
										// stream_size: expected_packets,
										data: {},
										odr: odr,
										mo: mode,
										fsr: fsr,
										device_temp: device_temp,
									}
									globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
									return;
								}
							}
							else{
								globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
							}
						}
						else{

							globalDevices[deviceAddr] = {
								// stream_size: expected_packets,
								data: {},
								odr: odr,
								mo: mode,
								fsr: fsr,
								device_temp: device_temp,
							}
							globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						}

						if(Object.keys(globalDevices[deviceAddr].data).length == expected_packets){
							var raw_data = new Array();
							for(const packet in globalDevices[deviceAddr].data){
								raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
							}
							var label = 0;

							var fft = new Array();
							var fft_concat = {};

							var en_axis_data = {};
							en_axis_data.x_offset = 0;
							en_axis_data.y_offset = 2;
							en_axis_data.z_offset = 4;
							en_axis_data.increment = 6;
							fft_concat = {x: [], y: [], z: []};

							switch(globalDevices[deviceAddr].fsr){
								case 0:
									fsr_text = "2g";
									break;
								case 1:
									fsr_text = "4g";
									break;
								case 2:
									fsr_text = "8g";
									break;
								case 3:
									fsr_text = "16g";
									break;
							}

							for(var i = 0; i < raw_data.length; i+=en_axis_data.increment){
								label++;

								if('x_offset' in en_axis_data){
									fft_concat.x.push(parseFloat((signInt(((raw_data[i+en_axis_data.x_offset]<<8)+(raw_data[i+en_axis_data.x_offset+1])), 16) / 100).toFixed(2)));
								}
								if('y_offset' in en_axis_data){
									fft_concat.y.push(parseFloat((signInt(((raw_data[i+en_axis_data.y_offset]<<8)+(raw_data[i+en_axis_data.y_offset+1])), 16) / 100).toFixed(2)));
								}
								if('z_offset' in en_axis_data){
									fft_concat.z.push(parseFloat((signInt(((raw_data[i+en_axis_data.z_offset]<<8)+(raw_data[i+en_axis_data.z_offset+1])), 16) / 100).toFixed(2)));
								}
							}
							var fft_concat_obj = {
								mode: mode,
								msg_type: msg_type,
								mac_address: deviceAddr,
								fsr: fsr_text,
								odr: globalDevices[deviceAddr].odr,
								device_temp: globalDevices[deviceAddr].device_temp,
								total_samples: label,
								data: fft_concat
							};
							sensor_data = fft_concat_obj;
							delete globalDevices[deviceAddr];
							if(this.hasOwnProperty('failure_no')){
								console.log('####falure no');
								console.log(this.failure_no);
							}

							return sensor_data;
						}
						else{
							return;
						}
					}else{

						globalDevices[deviceAddr] = {
							// stream_size: expected_packets,
							data: {},
							odr: odr,
							mo: mode,
							fsr: fsr,
							device_temp: device_temp,
						}
						globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						return;
					}
				}
				else if(payload[8] === 0){
					// mode byte most significant bit will indicate fft data.
					// console.log(d);
					let msg_type = '';
					switch(payload[9]){
						case 0:
							msg_type = 'Regular'
							break;
						case 1:
							msg_type = 'Cautionay';
							break;
						case 2:
							msg_type = 'Artwork';
							break;
						case 3:
							msg_type = 'Transient';
							break;
					}
					return {
						mode: payload[8],
						msg_type: msg_type,

						odr: payload.slice(10, 12).reduce(msbLsb),
						temperature: signInt(payload.slice(12, 14).reduce(msbLsb), 16) / 100,
						
						x_max_acceleration: payload.slice(14, 16).reduce(msbLsb),
						x_rms_acceleration: payload.slice(16, 18).reduce(msbLsb),
						x_max_velocity: payload.slice(18, 20).reduce(msbLsb) / 100,
						x_rms_velocity:payload.slice(20, 22).reduce(msbLsb) / 100,
						x_peak_one_Hz: payload.slice(22, 24).reduce(msbLsb),
						x_peak_two_Hz: payload.slice(24, 26).reduce(msbLsb),
						x_peak_three_Hz: payload.slice(26, 28).reduce(msbLsb),

						y_max_acceleration: payload.slice(28, 30).reduce(msbLsb),
						y_rms_acceleration: payload.slice(30, 32).reduce(msbLsb),
						y_max_velocity: payload.slice(32, 34).reduce(msbLsb) / 100,
						y_rms_velocity:payload.slice(34, 36).reduce(msbLsb) / 100,
						y_peak_one_Hz: payload.slice(36, 38).reduce(msbLsb),
						y_peak_two_Hz: payload.slice(38, 40).reduce(msbLsb),
						y_peak_three_Hz: payload.slice(40, 42).reduce(msbLsb),

						z_max_acceleration: payload.slice(42, 44).reduce(msbLsb),
						z_rms_acceleration: payload.slice(44, 46).reduce(msbLsb),
						z_max_velocity: payload.slice(46, 48).reduce(msbLsb) / 100,
						z_rms_velocity:payload.slice(48, 50).reduce(msbLsb) / 100,
						z_peak_one_Hz: payload.slice(50, 52).reduce(msbLsb),
						z_peak_two_Hz: payload.slice(52, 54).reduce(msbLsb),
						z_peak_three_Hz: payload.slice(54, 56).reduce(msbLsb),
					};
				}
				// else{
				// 	parsed.data = {'error': 'Vibration mode error'}
				// 	return parsed;
				// }
			},
			'parse_fly': (frame) => {
				return {
					'fly_rate': frame.slice(12, 14).reduce(msbLsb) + " Min.",
					'fsr': frame[14],
					'deadband': frame[15],
					'hardware_id': frame.slice(16, 19),
					'report_rate': frame.slice(19, 23).reduce(msbLsb) + " Sec.",
					'tx_lifetime_counter': frame.slice(23, 27).reduce(msbLsb),
					'machine_values': {
						'fly_rate': frame.slice(12, 14),
						'fsr':frame[14],
						'deadband': frame[15],
						'hardware_id': frame.slice(16, 19),
						'report_rate': frame.slice(19, 23),
						'tx_lifetime_counter': frame.slice(23, 27)
					}
				}
			}
		},
		'118': {
			name: 'Dual Pressure and Temperature Sensor',
			parse: (payload, parsed, mac) => {
				let res = {};

				res.pressure_s1 = signInt(payload.slice(8, 12).reduce(msbLsb),32)/100;
				res.temperature_s1 = signInt(payload.slice(12, 14).reduce(msbLsb),16)/100;
				if((payload[7] & 2)){
					res.error_s1 = 'Error: Sensor Probe 1 communication error';
				}

				res.pressure_s2 = signInt(payload.slice(14, 18).reduce(msbLsb),32)/100;
				res.temperature_s2 = signInt(payload.slice(18, 20).reduce(msbLsb),16)/100;
				if((payload[7] & 4)){
					res.error_s2 = 'Error: Sensor Probe 2 communication error';
				}
				return res;
			},
			'parse_fly': (frame) => {
				let psi_1;
				let psi_2;
				switch(frame[12]){
					case 0:
						psi_1 = "10 PSI"
						break;
					case 1:
						psi_1 = "20 PSI"
						break;
					case 2:
						psi_1 = "100 PSI"
						break;
					case 3:
						psi_1 = "500 PSI"
						break;
					case 4:
						psi_1 = "1000 PSI"
						break;
					case 5:
						psi_1 = "5000 PSI"
						break;
					case 6:
						psi_1 = "10000 PSI"
						break;
				}
				switch(frame[13]){
					case 0:
						psi_2 = "10 PSI"
						break;
					case 1:
						psi_2 = "20 PSI"
						break;
					case 2:
						psi_2 = "100 PSI"
						break;
					case 3:
						psi_2 = "500 PSI"
						break;
					case 4:
						psi_2 = "1000 PSI"
						break;
					case 5:
						psi_2 = "5000 PSI"
						break;
					case 6:
						psi_2 = "10000 PSI"
						break;
				}
				return {
					'firmware': frame[2],
					'sensor_1_fs': psi_1,
					'sensor_2_fs': psi_2,
					'auto_check_interval': frame.slice(14, 16).reduce(msbLsb) + " Sec.",
					'press_auto_check_percent': frame[17] + " %",
					'temp_auto_check_percent': frame[17] + " %",
					'hardware_id': frame.slice(18, 21),
					'sample_rate': frame.slice(21, 25).reduce(msbLsb) + " Sec.",
					'tx_life_counter': frame.slice(25, 29).reduce(msbLsb),
					'machine_values': {
						'firmware': frame[2],
						'sensor_1_fs': frame[12],
						'sensor_2_fs': frame[13],
						'auto_check_interval': frame.slice(14, 16),
						'press_auto_check_percent': frame[16],
						'temp_auto_check_percent': frame[17],
						'hardware_id': frame.slice(18, 21),
						'sample_rate': frame.slice(21, 25),
						'tx_life_counter': frame.slice(25, 29),
					}
				}
			}
		},
		'119': {
			name: 'Machine Runtime Hour Meter',
			parse: (d, payload) => {
				let firmware = payload[1];
				// if((payload[7] & 2) != 0){
				// 	console.log('Error found');
				// 	// parsed.data = {error: 'Error found, Accelerometer Probe may be unattached'};
				// 	let error = {error: 'Error found, Accelerometer'};
				// 	return error;
				// }
				let report_type = "Regular";
				switch(d[17]){
					case 0:
						report_type = "Regular";
						break;
					case 1:
						report_type = "Shift end";
						break;
					case 2:
						report_type = "Interrupt";
						break;
					case 3:
						report_type = "Threshold";
						break;
				}
				if ((payload[7] & 4)) {
					let res = {
						raw_data: "Enabled",
						accelerometer_counter: d.slice(0, 4).reduce(msbLsb),
						accelerometer_uptime: d.slice(4, 8).reduce(msbLsb),
						magnetometer_counter: d.slice(8, 12).reduce(msbLsb),
						magnetometer_uptime: d.slice(12, 16).reduce(msbLsb),
						input_acc: d[16] & 1 ? 1 : 0,
						input_mag: d[16] & 2 ? 1 : 0,
						report_type: report_type,
						rtc: [
							String(d[18]).padStart(2, '0'),
							String(d[19]).padStart(2, '0'),
							String(d[20]).padStart(2, '0')
						].join(':'),
						'temperature': d.slice(21, 23).reduce(msbLsb)/100,
						'data':{
							'x':[
								signInt(d.slice(23, 25).reduce(msbLsb),16)/100,
								signInt(d.slice(29, 31).reduce(msbLsb),16)/100,
								signInt(d.slice(35, 37).reduce(msbLsb),16)/100,
								signInt(d.slice(41, 43).reduce(msbLsb),16)/100,
								signInt(d.slice(47, 49).reduce(msbLsb),16)/100,
								signInt(d.slice(53, 55).reduce(msbLsb),16)/100,
								signInt(d.slice(59, 61).reduce(msbLsb),16)/100,
								signInt(d.slice(65, 67).reduce(msbLsb),16)/100,
								signInt(d.slice(71, 73).reduce(msbLsb),16)/100,
								signInt(d.slice(77, 79).reduce(msbLsb),16)/100
							],
							'y':[
								signInt(d.slice(25, 27).reduce(msbLsb),16)/100,
								signInt(d.slice(29, 31).reduce(msbLsb),16)/100,
								signInt(d.slice(37, 39).reduce(msbLsb),16)/100,
								signInt(d.slice(43, 45).reduce(msbLsb),16)/100,
								signInt(d.slice(49, 51).reduce(msbLsb),16)/100,
								signInt(d.slice(55, 57).reduce(msbLsb),16)/100,
								signInt(d.slice(61, 63).reduce(msbLsb),16)/100,
								signInt(d.slice(67, 69).reduce(msbLsb),16)/100,
								signInt(d.slice(73, 75).reduce(msbLsb),16)/100,
								signInt(d.slice(79, 81).reduce(msbLsb),16)/100
							],
							'z':[
								signInt(d.slice(27, 29).reduce(msbLsb),16)/100,
								signInt(d.slice(31, 33).reduce(msbLsb),16)/100,
								signInt(d.slice(37, 39).reduce(msbLsb),16)/100,
								signInt(d.slice(43, 45).reduce(msbLsb),16)/100,
								signInt(d.slice(51, 53).reduce(msbLsb),16)/100,
								signInt(d.slice(57, 59).reduce(msbLsb),16)/100,
								signInt(d.slice(63, 65).reduce(msbLsb),16)/100,
								signInt(d.slice(69, 71).reduce(msbLsb),16)/100,
								signInt(d.slice(75, 77).reduce(msbLsb),16)/100,
								signInt(d.slice(81, 83).reduce(msbLsb),16)/100
							]
						}
					};
					if((payload[7] & 2) != 0){
						res.error = 'Error: Accelerometer communication failure';
					}
					return res;
				}else{
					let res = {
						raw_data: "Disabled",
						accelerometer_counter: d.slice(0, 4).reduce(msbLsb),
						accelerometer_uptime: d.slice(4, 8).reduce(msbLsb),
						magnetometer_counter: d.slice(8, 12).reduce(msbLsb),
						magnetometer_uptime: d.slice(12, 16).reduce(msbLsb),
						input_acc: d[16] & 1 ? 1 : 0,
						input_mag: d[16] & 2 ? 1 : 0,
						report_type: report_type,
						rtc: [
							String(d[18]).padStart(2, '0'),
							String(d[19]).padStart(2, '0'),
							String(d[20]).padStart(2, '0')
						].join(':'),
						'temperature': d.slice(21, 23).reduce(msbLsb)/100
					};
					if((payload[7] & 2) != 0){
						res.error = 'Error: Accelerometer communication failure';
					}
					return res;
				}
			},
			'parse_fly': (frame) => {
				let reset_mode = "Disabled";
				switch(frame[35]){
					case 0:
						reset_mode = "Disabled";
						break;
					case 1:
						reset_mode = "Shift Ends";
						break;
					case 2:
						reset_mode = "Timeout";
						break;
				}
				let acc_odr = "10 Hz";
				switch(frame[37]){
					case 0:
						acc_odr = "10 Hz";
						break;
					case 1:
						acc_odr = "20 Hz";
						break;
					case 2:
						acc_odr = "50 Hz";
						break;
					case 3:
						acc_odr = "100 Hz";
						break;
					case 4:
						acc_odr = "200 Hz";
						break;
					case 5:
						acc_odr = "400 Hz";
						break;
				}
				let rtc_sampling_interval = "5 sec";
				switch(frame[36]){
					case 0:
						rtc_sampling_interval = "1 min";
						break;
					case 1:
						rtc_sampling_interval = "5 min";
						break;
					case 2:
						rtc_sampling_interval = "15 min";
						break;
					case 3:
						rtc_sampling_interval = "30 min";
						break;
					case 4:
						rtc_sampling_interval = "1 hour";
						break;
					case 5:
						rtc_sampling_interval = "2 hours";
						break;
					case 6:
						rtc_sampling_interval = "3 hours";
						break;
					case 7:
						rtc_sampling_interval = "6 hours";
						break;
					case 8:
						rtc_sampling_interval = "12 hours";
						break;
					case 9:
						rtc_sampling_interval = "5 sec";
						break;
					case 10:
						rtc_sampling_interval = "10 sec";
						break;
					case 11:
						rtc_sampling_interval = "15 sec";
						break;
					case 12:
						rtc_sampling_interval = "30 sec";
						break;
				}
				let acc_enabled_axis = 'Disabled All';
				switch(frame[40]){
					case 0:
						acc_enabled_axis = 'Disabled All';
						break;
					case 1:
						acc_enabled_axis = 'x axis';
						break;
					case 2:
						acc_enabled_axis = 'y axis';
						break;
					case 3:
						acc_enabled_axis = 'z axis';
						break;
					case 7:
						acc_enabled_axis = 'all axis';
						break;
				}
				let raw_acc_data = frame[41]?'Enabled':'Disabled';
				return {
					'firmware': frame[2],
					'accelerometer_threshold': (frame[16]* 32) + " mg",
					'debouncing_timeout': frame.slice(17, 19).reduce(msbLsb) + " msec",
					'accelero_state': frame[19]? "Enabled": "Disabled",
					'counter_threshold': frame.slice(20, 24).reduce(msbLsb),
					'trasnmit_on_change_status': frame[24]? "Enabled": "Disabled",
					'shift_end_1': [
						String(frame[25]).padStart(2, '0'),
						String(frame[26]).padStart(2, '0')
					].join(':'),
					'shift_end_2': [
						String(frame[27]).padStart(2, '0'),
						String(frame[28]).padStart(2, '0')
					].join(':'),
					'shift_end_3': [
						String(frame[29]).padStart(2, '0'),
						String(frame[30]).padStart(2, '0')
					].join(':'),
					'shift_end_4': [
						String(frame[31]).padStart(2, '0'),
						String(frame[32]).padStart(2, '0')
					].join(':'),
					'reset_timeout': frame.slice(33, 35).reduce(msbLsb) + " min",
					'counter_reset_mode': reset_mode,
					'sampling_interval': rtc_sampling_interval,
					'acc_odr': acc_odr,
					'interrupt_timeout': frame.slice(38, 40).reduce(msbLsb) + ' msec',
					'acc_enabled_axis': acc_enabled_axis,
					'raw_acc_data': raw_acc_data,
					'hardware_id': frame.slice(42, 45),
					'report_rate': frame.slice(45, 49).reduce(msbLsb) + " sec",
					'tx_life_counter': frame.slice(49, 53).reduce(msbLsb),
					'machine_values': {
						'firmware': frame[2],
						'accelerometer_threshold': (frame[16]* 32),
						'debouncing_timeout': frame.slice(17, 19),
						'accelero_state': frame[19],
						'counter_threshold': frame.slice(20, 24),
						'trasnmit_on_change_status': frame[24],
						'shift_end_1': [
							frame[25],
							frame[26]
						],
						'shift_end_2': [
							frame[27],
							frame[28]
						],
						'shift_end_3': [
							frame[29],
							frame[30]
						],
						'shift_end_4': [
							frame[31],
							frame[32]
						],
						'reset_timeout': frame.slice(33, 35),
						'counter_reset_mode': frame[35],
						'sampling_interval': frame[36],
						'acc_odr': frame[37],
						'interrupt_timeout': frame.slice(38, 40),
						'acc_enabled_axis': frame[40],
						'raw_acc_data': frame[41],
						'hardware_id': frame.slice(42, 45),
						'report_rate': frame.slice(45, 49),
						'tx_life_counter': frame.slice(49, 53)
					}
				}
			}
		},
		'120': {
			name: 'Wireless H2S Sensor',
			parse: (d) => {
				return {
					ppm: d.slice(0, 2).reduce(msbLsb),
					temperature: signInt(d.slice(2, 4).reduce(msbLsb), 16) / 100,
					humidity: signInt(d.slice(4, 6).reduce(msbLsb), 16) / 100
				};
			},
			'parse_fly': (frame) => {
				return {
					'firmware': frame[2],
					'h2s_threshold': frame[12],
					'always_on_status': frame[13],
					'hardware_id': frame.slice(14, 17),
					'sample_rate': frame.slice(17, 21).reduce(msbLsb) + " Sec.",
					'tx_life_counter': frame.slice(21, 25).reduce(msbLsb),
					'machine_values': {
						'firmware': frame[2],
						'h2s threshold': frame[12],
						'always_on_status': frame[13],
						'hardware_id': frame.slice(14, 17),
						'sample_rate': frame.slice(17, 21).reduce(msbLsb),
						'tx_life_counter': frame.slice(21, 25).reduce(msbLsb)
					}
				}
			}
		},
		'121':{
			name: 'Wireless Wood Moisture Sensor',
			parse: (d) => {
				return {
					wood_type: d[0],
					temperature: signInt(d.slice(1, 3).reduce(msbLsb), 16) / 100,
					humidity: d.slice(3, 5).reduce(msbLsb) / 100,
					wood_moisture: d.slice(5, 7).reduce(msbLsb) / 100,
					wood_resistance: (d.slice(7, 15)).reduce(msbLsb) / 100
				};
			},
			'parse_fly': (frame) => {
				return {
					'hardware_id': frame.slice(12, 15),
					'sample_rate': frame.slice(15, 19).reduce(msbLsb) + " Sec.",
					'tx_life_counter': frame.slice(19, 23).reduce(msbLsb),
					'machine_values': {
						'hardware_id': frame.slice(12, 15),
						'sample_rate': frame.slice(15, 19),
						'tx_life_counter': frame.slice(19, 23)
					}
				}
			}
		},
		'124': {
			name: 'Wireless EH Flow Sensor',
			parse: (d) => {
				return {
					volume_flow: signInt(d.slice(0, 4).reduce(msbLsb), 32) / 100,
					conductivity: d.slice(4, 8).reduce(msbLsb),
					flow_velocity: signInt(d.slice(8, 12).reduce(msbLsb), 32) / 100,
					pressure: signInt(d.slice(12, 16).reduce(msbLsb),32) / 100,
					// WARNING doesn't handle decimal values and will round to whole number.
					total_flow_1: d.slice(16, 24).reduce(msbLsb_to_SignedInt, 0n) / BigInt(100),
					total_flow_2: d.slice(24, 32).reduce(msbLsb_to_SignedInt, 0n) / BigInt(100),
					total_flow_3: d.slice(32, 40).reduce(msbLsb_to_SignedInt, 0n) / BigInt(100),
					battery_life: d.slice(40, 42).reduce(msbLsb),
					battery_state: d[42],
					total_flow_1_raw: d.slice(16, 24),
					total_flow_2_raw: d.slice(24, 32),
					total_flow_3_raw: d.slice(32, 40)
				};
			},
			'parse_fly': (frame) => {
				let frame_data = {};
				switch(frame[12]){
					case 0:
						frame_data.flow_unit = 'cm³/s';
						break;
					case 1:
						frame_data.flow_unit = 'cm³/min';
						break;
					case 2:
						frame_data.flow_unit = 'cm³/h';
						break;
					case 3:
						frame_data.flow_unit = 'cm³/d';
						break;
					case 4:
						frame_data.flow_unit = 'dm³/s';
						break;
					case 5:
						frame_data.flow_unit = 'dm³/min';
						break;
					case 6:
						frame_data.flow_unit = 'dm³/h';
						break;
					case 7:
						frame_data.flow_unit = 'dm³/d';
						break;
					case 8:
						frame_data.flow_unit = 'm³/s';
						break;
					case 9:
						frame_data.flow_unit = 'm³/min';
						break;
					case 10:
						frame_data.flow_unit = 'm³/h';
						break;
					case 11:
						frame_data.flow_unit = 'm³/d';
						break;
					case 12:
						frame_data.flow_unit = 'ml/s';
						break;
					case 13:
						frame_data.flow_unit = 'ml/min';
						break;
					case 14:
						frame_data.flow_unit = 'ml/h';
						break;
					case 15:
						frame_data.flow_unit = 'ml/d';
						break;
					case 16:
						unitStrframe_data.flow_uniting = 'l/s';
						break;
					case 17:
						frame_data.flow_unit = 'l/min';
						break;
					case 18:
						frame_data.flow_unit = 'l/h';
						break;
					case 19:
						frame_data.flow_unit = 'l/d';
						break;
					case 20:
						frame_data.flow_unit = 'hl/s';
						break;
					case 21:
						frame_data.flow_unit = 'hl/min';
						break;
					case 22:
						frame_data.flow_unit = 'hl/h';
						break;
					case 23:
						frame_data.flow_unit = 'hl/d';
						break;
					case 24:
						frame_data.flow_unit = 'Ml/s';
						break;
					case 25:
						frame_data.flow_unit = 'Ml/min';
						break;
					case 26:
						frame_data.flow_unit = 'Ml/h';
						break;
					case 27:
						frame_data.flow_unit = 'Ml/d';
						break;

					// US Customary & Imperial Flow Rates
					case 32:
						frame_data.flow_unit = 'af/s';
						break;
					case 33:
						frame_data.flow_unit = 'af/min';
						break;
					case 34:
						frame_data.flow_unit = 'af/h';
						break;
					case 35:
						frame_data.flow_unit = 'af/d';
						break;
					case 36:
						frame_data.flow_unit = 'ft³/s';
						break;
					case 37:
						frame_data.flow_unit = 'ft³/min';
						break;
					case 38:
						frame_data.flow_unit = 'ft³/h';
						break;
					case 39:
						frame_data.flow_unit = 'ft³/d';
						break;
					case 40:
						frame_data.flow_unit = 'fl oz/s (us)';
						break;
					case 41:
						frame_data.flow_unit = 'fl oz/min (us)';
						break;
					case 42:
						frame_data.flow_unit = 'fl oz/h (us)';
						break;
					case 43:
						frame_data.flow_unit = 'fl oz/d (us)';
						break;
					case 44:
						frame_data.flow_unit = 'gal/s (us)';
						break;
					case 45:
						frame_data.flow_unit = 'gal/min (us)';
						break;
					case 46:
						frame_data.flow_unit = 'gal/h (us)';
						break;
					case 47:
						frame_data.flow_unit = 'gal/d (us)';
						break;
					case 48:
						frame_data.flow_unit = 'Mgal/s (us)';
						break;
					case 49:
						frame_data.flow_unit = 'Mgal/min (us)';
						break;
					case 50:
						frame_data.flow_unit = 'Mgal/h (us)';
						break;
					case 51:
						frame_data.flow_unit = 'Mgal/d (us)';
						break;
					case 52:
						frame_data.flow_unit = 'bbl/s (us;liq.)';
						break;
					case 53:
						frame_data.flow_unit = 'bbl/min (us;liq.)';
						break;
					case 54:
						frame_data.flow_unit = 'bbl/h (us;liq.)';
						break;
					case 55:
						frame_data.flow_unit = 'bbl/d (us;liq.)';
						break;
					case 56:
						frame_data.flow_unit = 'bbl/s (us;beer)';
						break;
					case 57:
						frame_data.flow_unit = 'bbl/min (us;beer)';
						break;
					case 58:
						frame_data.flow_unit = 'bbl/h (us;beer)';
						break;
					case 59:
						frame_data.flow_unit = 'bbl/d (us;beer)';
						break;
					case 60:
						frame_data.flow_unit = 'bbl/s (us;oil)';
						break;
					case 61:
						frame_data.flow_unit = 'bbl/min (us;oil)';
						break;
					case 62:
						frame_data.flow_unit = 'bbl/h (us;oil)';
						break;
					case 63:
						frame_data.flow_unit = 'bbl/d (us;oil)';
						break;
					case 64:
						frame_data.flow_unit = 'bbl/s (us;tank)';
						break;
					case 65:
						frame_data.flow_unit = 'bbl/min (us;tank)';
						break;
					case 66:
						frame_data.flow_unit = 'bbl/h (us;tank)';
						break;
					case 67:
						frame_data.flow_unit = 'bbl/d (us;tank)';
						break;
					case 68:
						frame_data.flow_unit = 'gal/s (imp)';
						break;
					case 69:
						frame_data.flow_unit = 'gal/min (imp)';
						break;
					case 70:
						frame_data.flow_unit = 'gal/h (imp)';
						break;
					case 71:
						frame_data.flow_unit = 'gal/d (imp)';
						break;
					case 72:
						frame_data.flow_unit = 'Mgal/s (imp)';
						break;
					case 73:
						frame_data.flow_unit = 'Mgal/min (imp)';
						break;
					case 74:
						frame_data.flow_unit = 'Mgal/h (imp)';
						break;
					case 75:
						frame_data.flow_unit = 'Mgal/d (imp)';
						break;
					case 76:
						frame_data.flow_unit = 'bbl/s (imp;beer)';
						break;
					case 77:
						frame_data.flow_unit = 'bbl/min (imp;beer)';
						break;
					case 78:
						frame_data.flow_unit = 'bbl/h (imp;beer)';
						break;
					case 79:
						frame_data.flow_unit = 'bbl/d (imp;beer)';
						break;
					case 80:
						frame_data.flow_unit = 'bbl/s (imp;oil)';
						break;
					case 81:
						frame_data.flow_unit = 'bbl/min (imp;oil)';
						break;
					case 82:
						frame_data.flow_unit = 'bbl/h (imp;oil)';
						break;
					case 83:
						frame_data.flow_unit = 'bbl/d (imp;oil)';
						break;
					case 88:
						frame_data.flow_unit = 'kgal/s (us)';
						break;
					case 89:
						frame_data.flow_unit = 'kgal/min (us)';
						break;
					case 90:
						frame_data.flow_unit = 'kgal/h (us)';
						break;
					case 91:
						frame_data.flow_unit = 'kgal/d (us)';
						break;
					case 92:
						frame_data.flow_unit = 'MMft³/s';
						break;
					case 93:
						frame_data.flow_unit = 'MMft³/min';
						break;
					case 94:
						frame_data.flow_unit = 'MMft³/h';
						break;
					case 96:
						frame_data.flow_unit = 'Mft³/d';
						break;
				}
				switch(frame[13]){
					case 0:
						frame_data.pressure_unit = 'bar';
						break;
					case 1:
						frame_data.pressure_unit = 'psi a';
						break;
					case 2:
						frame_data.pressure_unit = 'bar g';
						break;
					case 3:
						frame_data.pressure_unit = 'psi g';
						break;
					case 4:
						frame_data.pressure_unit = 'Pa a';
						break;
					case 5:
						frame_data.pressure_unit = 'kPa a';
						break;
					case 6:
						frame_data.pressure_unit = 'MPa a';
						break;
					case 7:
						frame_data.pressure_unit = 'Pa g';
						break;
					case 8:
						frame_data.pressure_unit = 'kPa g';
						break;
					case 9:
						frame_data.pressure_unit = 'MPa g';
						break;
				}
				switch(frame[14]){
					case 1:
						frame_data.conductivity_unit = 'MS/m';
						break;
					case 2:
						frame_data.conductivity_unit = 'kS/m';
						break;
					case 3:
						frame_data.conductivity_unit = 'S/m';
						break;
					case 4:
						frame_data.conductivity_unit = 'S/cm';
						break;
					case 5:
						frame_data.conductivity_unit = 'mS/m';
						break;
					case 6:
						frame_data.conductivity_unit = 'mS/cm';
						break;
					case 7:
						frame_data.conductivity_unit = 'μS/m';
						break;
					case 8:
						frame_data.conductivity_unit = 'μS/cm';
						break;
					case 9:
						frame_data.conductivity_unit = 'μS/mm';
						break;
					case 10:
						frame_data.conductivity_unit = 'nS/cm';
						break;
				}

				frame_data.totalizer_unit = [];

				for (let index = 0; index < 3; index++) {
					switch(frame[index + 15]){
						case 0:
							frame_data.totalizer_unit[index] = 'cm³';
							break;
						case 1:
							frame_data.totalizer_unit[index] = 'dm³';
							break;
						case 2:
							frame_data.totalizer_unit[index] = 'm³';
							break;
						case 3:
							frame_data.totalizer_unit[index] = 'ml';
							break;
						case 4:
							frame_data.totalizer_unit[index] = 'l';
							break;
						case 5:
							frame_data.totalizer_unit[index] = 'hl';
							break;
						case 6:
							frame_data.totalizer_unit[index] = 'Ml Mega';
							break;
						case 8:
							frame_data.totalizer_unit[index] = 'af';
							break;
						case 9:
							frame_data.totalizer_unit[index] = 'ft³';
							break;
						case 10:
							frame_data.totalizer_unit[index] = 'fl oz (us)';
							break;
						case 11:
							frame_data.totalizer_unit[index] = 'gal (us)';
							break;
						case 12:
							frame_data.totalizer_unit[index] = 'Mgal (us)';
							break;
						case 13:
							frame_data.totalizer_unit[index] = 'bbl (us;liq.)';
							break;
						case 14:
							frame_data.totalizer_unit[index] = 'bbl (us;beer)';
							break;
						case 15:
							frame_data.totalizer_unit[index] = 'bbl (us;oil)';
							break;
						case 16:
							frame_data.totalizer_unit[index] = 'bbl (us;tank)';
							break;
						case 17:
							frame_data.totalizer_unit[index] = 'gal (imp)';
							break;
						case 18:
							frame_data.totalizer_unit[index] = 'Mgal (imp)';
							break;
						case 19:
							frame_data.totalizer_unit[index] = 'bbl (imp;beer)';
							break;
						case 20:
							frame_data.totalizer_unit[index] = 'bbl (imp;oil)';
							break;
						case 22:
							frame_data.totalizer_unit[index] = 'kgal (us)';
							break;
						case 23:
							frame_data.totalizer_unit[index] = 'Mft³';
							break;
					}
					
				}
				return {
					'firmware': frame[2],
					'flow_unit': frame_data.flow_unit,
					'pressure_unit': frame_data.pressure_unit,
					'conductivity_unit': frame_data.conductivity_unit,
					'totalizer_1_unit': frame_data.totalizer_unit[0],
					'totalizer_2_unit': frame_data.totalizer_unit[1],
					'totalizer_3_unit': frame_data.totalizer_unit[2],
					'hardware_id': frame.slice(18, 21),
					'sample_rate': frame.slice(21, 25).reduce(msbLsb),
					'tx_life_counter': frame.slice(25, 29).reduce(msbLsb),
					'machine_values': {
						'firmware': frame[2],
						'flow_unit': frame[12],
						'pressure_unit': frame[13],
						'conductivity_unit': frame[14],
						'totalizer_1_unit': frame[15],
						'totalizer_2_unit': frame[16],
						'totalizer_3_unit': frame[17],
						'hardware_id': frame.slice(18, 21),
						'sample_rate': frame.slice(21, 25),
						'tx_life_counter': frame.slice(25, 29)
					}
				}
			}
		},
		'180': {
			name: 'C1D2 One Channel Vibration Plus',
			parse: (payload, parsed, mac) => {
				return parent.sensor_types[80].parse(payload, parsed, mac);
			},
			parse_fly: (payload, parsed, mac) => {
				return parent.sensor_types[80].parse_fly(payload, parsed, mac);
			},
		},
		'181': {
			name: 'C1D2 Two Channel Vibration Plus',
			parse: (payload, parsed, mac) => {
				return parent.sensor_types[81].parse(payload, parsed, mac);
			},
			parse_fly: (payload, parsed, mac) => {
				return parent.sensor_types[81].parse_fly(payload, parsed, mac);
			},
		},
		'200': {
			name: '4-20mA Pass Through',
			parse: (d) => {
				var adc1 = signInt(d.slice(0, 2).reduce(msbLsb));
				var adc2 = signInt(d.slice(2, 4).reduce(msbLsb));
				var dac1 = signInt(d.slice(4, 6).reduce(msbLsb));
				return {
					adc1: adc1,
					adc2: adc2,
					dac1: dac1,
					mA1: parseFloat((adc1/100.00).toFixed(2)),
					raw_adc: adc2,
					raw_dac: dac1
				};
			}
		},
		'202': {
			name: 'Wireless Weather Station',
			parse: (payload, parsed, mac) => {
				return {
					Temp: signInt(payload.slice(8, 12).reduce(msbLsb), 32) / 100,
					Humid: signInt(payload.slice(12, 16).reduce(msbLsb), 32) / 100,
					Pressure: signInt(payload.slice(16, 20).reduce(msbLsb), 32) / 100,
					WindSpd: signInt(payload.slice(20, 24).reduce(msbLsb),32) / 100,
					WindDir: signInt(payload.slice(24, 28).reduce(msbLsb),32) / 100,
					reserve: payload[7]
				};
			}
		},
		'211': {
			name: 'DO and Flow Sensor',
			parse: (payload, parsed, mac) => {
				if(payload[0] == 0){ // regular
					return {
						msg_type: 'regular',
						temperature: signInt(payload.slice(1, 3).reduce(msbLsb), 16),
						oxygen_saturation_percent: payload.slice(3, 7).reduce(msbLsb),
						oxygen_ppm: payload.slice(7, 11).reduce(msbLsb),
						oxygen_mg_l: payload.slice(11, 15).reduce(msbLsb)
					};
				}
				else{ // theshold
					let solenoid_status = payload[2];
					if(solenoid_status == 1){ // Solenoid On
						return {
							msg_type: 'threshold',
							solenoid_number: payload[1],
							solenoid_status: 'on',
							temperature: signInt(payload.slice(3, 5).reduce(msbLsb), 16),
							oxygen_saturation_percent: payload.slice(5, 9).reduce(msbLsb),
							oxygen_ppm: payload.slice(9, 13).reduce(msbLsb),
							oxygen_mg_l: payload.slice(13, 17).reduce(msbLsb),
							flow_rate: payload.slice(17, 21).reduce(msbLsb)
						};
					} else{ // Solenoid Off
						return {
							msg_type: 'threshold',
							solenoid_number: payload[1],
							solenoid_status: 'off',
							temperature: signInt(payload.slice(3, 5).reduce(msbLsb), 16),
							oxygen_saturation_percent: payload.slice(5, 9).reduce(msbLsb),
							oxygen_ppm: payload.slice(9, 13).reduce(msbLsb),
							oxygen_mg_l: payload.slice(13, 17).reduce(msbLsb)
						};
					}
				}
			},
			'parse_fly': (frame) => {
				return {
					'firmware': frame[2],
					'hardware_id': frame.slice(12, 15),
					'report_rate': frame.slice(15, 19).reduce(msbLsb) + "Sec",
					'tx_life_counter': frame.slice(19, 23).reduce(msbLsb),
					'machine_values': {
						'firmware': frame[2],
						'hardware_id': frame.slice(12, 15),
						'report_rate': frame.slice(15, 19),
						'tx_life_counter': frame.slice(19, 23)
					}
				}
			}
		},
		'217': {
			name: 'Wireless Weight Scale',
			parse: (d, parsed, mac) => {
				let firmware = d[1];
				let response = {};
				if(firmware > 8){
					if(d[7] & 2){
						console.log('Error found');
						// parsed.data = {error: 'Error found, Load cell comm error'};
						response.error = 'Error found, Load cell comm error';
					}
				}else{
					if(d[7] == 254){
						console.log('Error found');
						// parsed.data = {error: 'Error found, Load cell comm error'};
						response.error = 'Error found, Load cell comm error';
					}
				}
				
				if(firmware > 4){
					let unit = 'Lb';
					switch(d[12]){
						case 1:
							unit = 'Kg';
						break;
						case 2:
							unit = 'Lb';
						break;
					}
					response.weight = signInt(d.slice(8, 12).reduce(msbLsb), 32) / 100;
					response.unit = unit;
					response.raw_adc = signInt(d.slice(13, 17).reduce(msbLsb), 32);
					response.weight_factor_mult = signInt(d.slice(17, 21).reduce(msbLsb), 32);
					return response;
					// return{
					// 	weight: signInt(d.slice(8, 12).reduce(msbLsb), 32) / 100,
					// 	unit: unit,
					// 	raw_adc: signInt(d.slice(13, 17).reduce(msbLsb), 32),
					// 	weight_factor_mult: signInt(d.slice(17, 21).reduce(msbLsb), 32)
					// }
				}
				else {
					response.weight = signInt(d.slice(8, 12).reduce(msbLsb), 32) / 100;
					return response;
					// return {
					// 	weight: signInt(d.slice(8, 12).reduce(msbLsb), 32) / 100
					// };
				}
			},
			'parse_fly': (frame) => {
				if(frame[2] > 5){
					let date = [
						String(frame.slice(20, 22).reduce(msbLsb)),
						String(frame[22]).padStart(2, '0'),
						String(frame[23]).padStart(2, '0')
					].join('-');
					let time = [
						String(frame[24]).padStart(2, '0'),
						String(frame[25]).padStart(2, '0'),
						String(frame[26]).padStart(2, '0')
					].join(':');
					let screen_on_time = "always_off";
					switch(frame[27]){
						case 0:
							screen_on_time = "always_off";
						break;
						case 255:
							screen_on_time = "always_on";
						break;
						default:
							screen_on_time = frame[27] + "Sec";
						break;
					}
					let unit = "Lb";
					switch(frame[28]){
						case 1:
							unit = "Lb";
						break;
						case 2:
							unit = "Kg";
						break;
					}
					return {
						'firmware': frame[2],
						'calibration_tare': frame.slice(12, 16).reduce(msbLsb),
						'tare_counter': frame.slice(16, 20).reduce(msbLsb),
						'timestamp': date + "T" + time,
						'screen_on_time': screen_on_time,
						'weight_unit': unit,
						'calibration_factor': frame.slice(29, 33).reduce(msbLsb),
						'hardware_id': frame.slice(33, 36),
						'report_rate': frame.slice(36, 40).reduce(msbLsb) + "Sec",
						'tx_life_counter': frame.slice(40, 44).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'calibration_tare': frame.slice(12, 16),
							'tare_counter': frame.slice(16, 20),
							'timestamp': date + "T" + time,
							'screen_on_time': screen_on_time,
							'weight_unit': unit,
							'calibration_factor': frame.slice(29, 33),
							'hardware_id': frame.slice(33, 36),
							'report_rate': frame.slice(36, 40),
							'tx_life_counter': frame.slice(40, 44)
						}
					}
				} else if(frame[2] > 4){
					let date = [
						String(frame.slice(20, 22).reduce(msbLsb)),
						String(frame[22]).padStart(2, '0'),
						String(frame[23]).padStart(2, '0')
					].join('-');
					let time = [
						String(frame[24]).padStart(2, '0'),
						String(frame[25]).padStart(2, '0'),
						String(frame[26]).padStart(2, '0')
					].join(':');
					let screen_on_time = "always_off";
					switch(frame[27]){
						case 0:
							screen_on_time = "always_off";
						break;
						case 255:
							screen_on_time = "always_on";
						break;
						default:
							screen_on_time = frame[27] + "Sec";
						break;
					}
					let unit = "Lb";
					switch(frame[28]){
						case 1:
							unit = "Lb";
						break;
						case 2:
							unit = "Kg";
						break;
					}
					return {
						'firmware': frame[2],
						'calibration_tare': frame.slice(12, 16).reduce(msbLsb),
						'tare_counter': frame.slice(16, 20).reduce(msbLsb),
						'timestamp': date + "T" + time,
						'screen_on_time': screen_on_time,
						'weight_unit': unit,
						'hardware_id': frame.slice(29, 32),
						'report_rate': frame.slice(32, 36).reduce(msbLsb) + "Sec",
						'tx_life_counter': frame.slice(36, 40).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'calibration_tare': frame.slice(12, 16),
							'tare_counter': frame.slice(16, 20),
							'timestamp': date + "T" + time,
							'screen_on_time': screen_on_time,
							'weight_unit': unit,
							'hardware_id': frame.slice(29, 32),
							'report_rate': frame.slice(32, 36),
							'tx_life_counter': frame.slice(36, 40)
						}
					}
				}else if(frame[2] == 4){
					let date = [
						String(frame.slice(20, 22).reduce(msbLsb)),
						String(frame[22]).padStart(2, '0'),
						String(frame[23]).padStart(2, '0')
					].join('-');
					let time = [
						String(frame[24]).padStart(2, '0'),
						String(frame[25]).padStart(2, '0'),
						String(frame[26]).padStart(2, '0')
					].join(':');
					return {
						'firmware': frame[2],
						'calibration_tare': frame.slice(12, 16).reduce(msbLsb),
						'tare_counter': frame.slice(16, 20).reduce(msbLsb),
						'timestamp': date + 'T' + time,
						'hardware_id': frame.slice(27, 30),
						'report_rate': frame.slice(30, 34).reduce(msbLsb) + "Sec",
						'tx_life_counter': frame.slice(34, 38).reduce(msbLsb),
						'machine_values': {
							'firmware': frame[2],
							'calibration_tare': frame.slice(12, 16),
							'tare_counter': frame.slice(16, 20),
							'date': frame.slice(20, 27),
							'hardware_id': frame.slice(27, 30),
							'report_rate': frame.slice(30, 34),
							'tx_life_counter': frame.slice(34, 38)
						}
					}
				}
			}
		},
		'270': {
			name: 'Custom Salinity DO sensor',
			parse: (d) => {
				return {
					sensor_status: d[0],
					do_temperature_1: d.slice(1, 3).reduce(msbLsb) / 100,
					do_temperature_2: d.slice(3, 5).reduce(msbLsb) / 100,
					do_temperature_3: d.slice(5, 7).reduce(msbLsb) / 100,
					do_temperature_4: d.slice(7, 9).reduce(msbLsb) / 100,
					ec_temperature_1: d.slice(9, 11).reduce(msbLsb) / 100,
					ec_temperature_2: d.slice(11, 13).reduce(msbLsb) / 100,
					ec_temperature_3: d.slice(13, 15).reduce(msbLsb) / 100,
					ec_temperature_4: d.slice(15, 17).reduce(msbLsb) / 100,
					do_saturation_perc_1: d.slice(17, 19).reduce(msbLsb) / 100,
					do_saturation_perc_2: d.slice(19, 21).reduce(msbLsb) / 100,
					do_saturation_perc_3: d.slice(21, 23).reduce(msbLsb) / 100,
					do_saturation_perc_4: d.slice(23, 25).reduce(msbLsb) / 100,
					do_ppm_1: d.slice(25, 27).reduce(msbLsb) / 100,
					do_ppm_2: d.slice(27, 29).reduce(msbLsb) / 100,
					do_ppm_3: d.slice(29, 31).reduce(msbLsb) / 100,
					do_ppm_4: d.slice(31, 33).reduce(msbLsb) / 100,
					do_mgl_1: d.slice(33, 35).reduce(msbLsb) / 100,
					do_mgl_2: d.slice(35, 37).reduce(msbLsb) / 100,
					do_mgl_3: d.slice(37, 39).reduce(msbLsb) / 100,
					do_mgl_4: d.slice(39, 41).reduce(msbLsb) / 100,
					ec_conductivity_1: d.slice(41, 43).reduce(msbLsb) / 100,
					ec_conductivity_2: d.slice(43, 45).reduce(msbLsb) / 100,
					ec_conductivity_3: d.slice(45, 47).reduce(msbLsb) / 100,
					ec_conductivity_4: d.slice(47, 49).reduce(msbLsb) / 100,
					ec_salinity_ppt_1: d.slice(49, 57).reduce(msbLsb) / 100,
					ec_salinity_ppt_2: d.slice(57, 65).reduce(msbLsb) / 100,
					ec_salinity_ppt_3: d.slice(65, 73).reduce(msbLsb) / 100,
					ec_salinity_ppt_4: d.slice(73, 81).reduce(msbLsb) / 100,
					ec_tds_1: d.slice(81, 85).reduce(msbLsb) / 100,
					ec_tds_2: d.slice(85, 89).reduce(msbLsb) / 100,
					ec_tds_3: d.slice(89, 93).reduce(msbLsb) / 100,
					ec_tds_4: d.slice(93, 97).reduce(msbLsb) / 100
				};
			},
			'parse_fly': (frame) => {
				return {
					'firmware': frame[2],
					'do_bootup_time': frame[12] + "Sec",
					'ec_bootup_time': frame[13] + "Sec",
					'device_ids_do_sensors': frame.slice(14, 18),
					'device_ids_ec_sensors': frame.slice(18, 22),
					'hardware_id': frame.slice(22, 25),
					'report_rate': frame.slice(25, 29).reduce(msbLsb) + "Sec",
					'tx_life_counter': frame.slice(29, 33).reduce(msbLsb),
					'machine_values': {
						'firmware': frame[2],
						'do_bootup_time': frame[12],
						'ec_bootup_time': frame[13],
						'device_ids_do_sensors': frame.slice(14, 18),
						'device_ids_ec_sensors': frame.slice(18, 22),
						'hardware_id': frame.slice(22, 25),
						'report_rate': frame.slice(25, 29),
						'tx_life_counter': frame.slice(29, 33)
					}
				}
			}
		},
		'502': {
			name: 'Custom Environmental Sensor',
			parse: (d, full) => {
				reserve = full[7];
				if (reserve == 0xAA){
					var obj = {};
					for(i = 0; i < 18; i++){
						var label = 'sound'+i;
						obj[label] = d[i];
					}
					return obj;
				}else{
					return {

						temperature: signInt(d.slice(0, 2).reduce(msbLsb), 16) / 100,
						pressure: d.slice(2, 6).reduce(msbLsb) / 100,
						humidity: d.slice(6, 10).reduce(msbLsb) / 1000,
						gas_resistance: d.slice(10, 14).reduce(msbLsb),
						iaq: d.slice(14, 16).reduce(msbLsb),
						light: d.slice(16, 18).reduce(msbLsb),
						sound: d[18]
					};
				}
			}
		},

		'505': {
			'name': 'Custom SAP 1-Channel Current Monitor',
			parse: (d) => {
				return {
					channel_1_rms: d.slice(0, 3).reduce(msbLsb),
					channel_1_max: d.slice(4, 7).reduce(msbLsb),
					channel_1_min: d.slice(8, 11).reduce(msbLsb)
				};
			}
		},

		'506': {
			'name': 'Custom SAP 3-Channel Current Monitor',
			parse: (d) => {
				return {
					channel_1_rms: d.slice(0, 3).reduce(msbLsb),
					channel_1_max: d.slice(4, 7).reduce(msbLsb),
					channel_1_min: d.slice(8, 11).reduce(msbLsb),
					channel_2_rms: d.slice(12, 15).reduce(msbLsb),
					channel_2_max: d.slice(16, 19).reduce(msbLsb),
					channel_2_min: d.slice(20, 23).reduce(msbLsb),
					channel_3_rms: d.slice(24, 27).reduce(msbLsb),
					channel_3_max: d.slice(28, 31).reduce(msbLsb),
					channel_3_min: d.slice(32, 35).reduce(msbLsb)
				};
			}
		},

		'507': {
			'name': 'Custom SAP 7-Channel Current Monitor',
			parse: (d) => {
				return {
					channel_1_rms: d.slice(0, 3).reduce(msbLsb),
					channel_1_max: d.slice(4, 7).reduce(msbLsb),
					channel_1_min: d.slice(8, 11).reduce(msbLsb),
					channel_2_rms: d.slice(12, 15).reduce(msbLsb),
					channel_2_max: d.slice(16, 19).reduce(msbLsb),
					channel_2_min: d.slice(20, 23).reduce(msbLsb),
					channel_3_rms: d.slice(24, 27).reduce(msbLsb),
					channel_3_max: d.slice(28, 31).reduce(msbLsb),
					channel_3_min: d.slice(32, 35).reduce(msbLsb),
					channel_4_rms: d.slice(36, 39).reduce(msbLsb),
					channel_4_max: d.slice(40, 43).reduce(msbLsb),
					channel_4_min: d.slice(44, 47).reduce(msbLsb),
					channel_5_rms: d.slice(48, 51).reduce(msbLsb),
					channel_5_max: d.slice(52, 55).reduce(msbLsb),
					channel_5_min: d.slice(56, 59).reduce(msbLsb),
					channel_6_rms: d.slice(60, 63).reduce(msbLsb),
					channel_6_max: d.slice(64, 67).reduce(msbLsb),
					channel_6_min: d.slice(68, 71).reduce(msbLsb),
					channel_7_rms: d.slice(72, 75).reduce(msbLsb),
					channel_7_max: d.slice(76, 79).reduce(msbLsb),
					channel_7_min: d.slice(80, 83).reduce(msbLsb)



				};
			}
		},
		'510': {
			name: 'GreenLight',
			parse: (d) => {
				var adc = d.slice(0, 2).reduce(msbLsb);
				return {
					mA: adc /100.00
				};
			}
		},
		'515': {
			name: 'Multi-Channel Current Sensor',
			parse: (d, parsed, mac) => {
				let bank = d[9];
				let bank_total = d[8];

				// reserve byte is d[4]
				// reserve byte errors and meaning
				// 0x00(Success + no OTF) ,
				// 0x01 (Success + OTF) ,
				// 0x02 (Error + no OTF),
				// 0x03 (Error + OTF)
				if(d[7] & 15 > 1){
					console.log('!-----!');
					console.log('515 Error Detected');
					console.log(d[7]);
					console.log('!-----!');
				}

				// Set the last packet counter context default to 280 as this is out of range
				// 280 will never be true
				if(!globalDevices.hasOwnProperty('last_packet_counter')){
					globalDevices['last_packet_counter'] = {};
				}
				if(!globalDevices['last_packet_counter'].hasOwnProperty(mac)){
					globalDevices['last_packet_counter'][mac] = 280;
				}

				// If there is no memory buffer for this device the create one
				if(!globalDevices.hasOwnProperty(mac)) {
					globalDevices[mac] = {};
					globalDevices[mac].data = {};
				}else{
					// If we can detect that this packet is part of a new packet,
					// send old mem buffer and restart
					// object.forEach loops through all and does not allow breaks
					// ideally we can switch to
					let stream_keys = Object.keys(globalDevices[mac].data);
					let less_than = (element) => bank < element;
					if(stream_keys.some(less_than)){
						let sensor_data = {};
						let sensor_payload_length = 54;
						for(let current_bank = 1; current_bank<=bank_total; current_bank++){
							for(let bindex = 0; bindex < sensor_payload_length; bindex+=9){
								// if the packet for a bank exists translate the data
								if(globalDevices[mac].data.hasOwnProperty(current_bank)){
									sensor_data[(bindex/9+6*(current_bank-1))+1] = {
										rms: globalDevices[mac].data[current_bank].slice(bindex, bindex+3).reduce(msbLsb),
										max: globalDevices[mac].data[current_bank].slice(bindex+3, bindex+6).reduce(msbLsb),
										min: globalDevices[mac].data[current_bank].slice(bindex+6, bindex+9).reduce(msbLsb)
									}
								}else{
									// If the buffer does not have this bank's packet
									// set default values of -1
									sensor_data[(bindex/9+6*(current_bank-1))+1] = {
										rms: -1,
										max: -1,
										min: -1
									}
								}
							}
						}

						// let sensor_data = this.build_515_data(bank, bank_total, mac);

						// Removed as not doing anything due to packet recovery attempt
						// delete globalDevices[mac];
						// globalDevices[mac] = {};
						// globalDevices[mac].data = {};

						// If we can only recover the last item in a bank
						// just consider the stream lost
						// to recover the last bank we would need to async call
						// this method again and it is not worth the complexity
						// unless this is absolutely necessary
						if(bank != bank_total){
							delete globalDevices[mac];
							globalDevices[mac] = {};
							globalDevices[mac].data = {};
							globalDevices[mac].data[bank] = d.slice(10,64);
						}else{
							delete globalDevices[mac];
						}
						return sensor_data;
					}
				}
				globalDevices[mac].data[bank] = d.slice(10,64);
				if(bank == bank_total && globalDevices['last_packet_counter'][mac] != d[4]){
					let sensor_data = {};
					let sensor_payload_length = 54;
					for(let current_bank = 1; current_bank<=bank_total; current_bank++){
						for(let bindex = 0; bindex < sensor_payload_length; bindex+=9){
							// if the packet for a bank exists translate the data
							if(globalDevices[mac].data.hasOwnProperty(current_bank)){
								sensor_data[(bindex/9+6*(current_bank-1))+1] = {
									rms: globalDevices[mac].data[current_bank].slice(bindex, bindex+3).reduce(msbLsb),
									max: globalDevices[mac].data[current_bank].slice(bindex+3, bindex+6).reduce(msbLsb),
									min: globalDevices[mac].data[current_bank].slice(bindex+6, bindex+9).reduce(msbLsb)
								}
							}else{
								// If the buffer does not have this bank's packet
								// set default values of -1
								sensor_data[(bindex/9+6*(current_bank-1))+1] = {
									rms: -1,
									max: -1,
									min: -1
								}
							}
						}
					}
					globalDevices['last_packet_counter'][mac] = d[4];
					delete globalDevices[mac];
					return sensor_data;
				}else if(bank == bank_total && globalDevices['last_packet_counter'][mac] == d[4]){
					// This section of code was added to combat mystery repeat packet for last bank.
					delete globalDevices[mac];
				}
			}
		},
		'519': {
			name: 'Type 519 - Vibration',
			parse: (payload, parsed, mac) => {
				if(payload[7] >> 1 != 0){
					console.log('Error found');
					parsed.data = {error: 'Error found, Sensor Probe may be unattached'};
					return parsed;
				}

				if(payload[8] === 1){
					var deviceAddr = mac;
					var firmware = payload[1];
					var hour = payload[11];
					var minute = payload[12];
					var expected_packets = payload[19];
					var current_packet = payload[20];
					var sdata_start = 21;

					if(globalDevices.hasOwnProperty(deviceAddr) || expected_packets == 1){
						if(expected_packets != 1){
							// if a packet is already stored with the same packet ID, or if packet ID is 1, or if current packet ID is not one more than last packet ID
							if(current_packet in globalDevices[deviceAddr].data || current_packet == 1 || !(((current_packet&127)-1) in globalDevices[deviceAddr].data)) {
								console.log('-----');
								console.log('bad packet breakdown deleting stream');
								console.log(current_packet);
								console.log(expected_packets);
								console.log(current_packet in globalDevices[deviceAddr].data);
								console.log(current_packet == 1);
								console.log(!((current_packet-1) in globalDevices[deviceAddr].data));
								if(this.hasOwnProperty('failure_no')){
									this.failure_no = this.failure_no + 1;
								}
								else{
									this.failure_no = 1;
								}
								if(this.hasOwnProperty('failure_no')){
									console.log('####falure no');
									console.log(this.failure_no);
								}
								// console.log(globalDevices[deviceAddr].data);
								delete globalDevices[deviceAddr];
								if(current_packet != 1){
									return;
								} else{

									var mode = payload[8];
									var odr = payload[9];
									var en_axis = payload[10] & 7;
									var fsr = payload[10] >> 5;
									var device_temp = signInt(msbLsb(payload[13], payload[14]))/100;
									var adc_1_raw = msbLsb(payload[15], payload[16]);
									var adc_2_raw = msbLsb(payload[17], payload[18]);


									switch(odr){
										case 6:
											odr = 50;
											break;
										case 7:
											odr = 100;
											break;
										case 8:
											odr = 200;
											break;
										case 9:
											odr = 400;
											break;
										case 10:
											odr = 800;
											break;
										case 11:
											odr = 1600;
											break;
										case 12:
											odr = 3200;
											break;
										case 13:
											odr = 6400;
											break;
										case 14:
											odr = 12800;
											break;
										case 15:
											odr = 25600;
											break;
										default:
											odr = 0;
									}

									globalDevices[deviceAddr] = {
										// stream_size: expected_packets,
										data: {},
										odr: odr,
										mo: mode,
										en_axis: en_axis,
										fsr: fsr,
										hour: hour,
										minute: minute,
										adc_1_raw: adc_1_raw,
										adc_2_raw: adc_2_raw,
										device_temp: device_temp,
									}
									globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
									return;
								}
							}
							else{
								globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
							}
						}
						else{
							var mode = payload[8];
							var odr = payload[9];
							var en_axis = payload[10] & 7;
							var fsr = payload[10] >> 5;
							var device_temp = signInt(msbLsb(payload[13], payload[14]))/100;
							var adc_1_raw = msbLsb(payload[15], payload[16]);
							var adc_2_raw = msbLsb(payload[17], payload[18]);


							switch(odr){
								case 6:
									odr = 50;
									break;
								case 7:
									odr = 100;
									break;
								case 8:
									odr = 200;
									break;
								case 9:
									odr = 400;
									break;
								case 10:
									odr = 800;
									break;
								case 11:
									odr = 1600;
									break;
								case 12:
									odr = 3200;
									break;
								case 13:
									odr = 6400;
									break;
								case 14:
									odr = 12800;
									break;
								case 15:
									odr = 25600;
									break;
								default:
									odr = 0;
							}

							globalDevices[deviceAddr] = {
								// stream_size: expected_packets,
								data: {},
								odr: odr,
								mo: mode,
								fsr: fsr,
								hour: hour,
								minute: minute,
								adc_1_raw: adc_1_raw,
								adc_2_raw: adc_2_raw,
								device_temp: device_temp,
							}
							globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						}
						if(Object.keys(globalDevices[deviceAddr].data).length == expected_packets){
							var raw_data = new Array();
							for(const packet in globalDevices[deviceAddr].data){
								raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
							}
							var label = 0;

							var fft = new Array();
							var fft_concat = {};

							var en_axis_data = {};
							switch (globalDevices[deviceAddr].en_axis){
								case 1:
									en_axis_data.x_offset = 0;
									en_axis_data.increment = 2;
									break;
								case 2:
									en_axis_data.y_offset = 0;
									en_axis_data.increment = 2;
									break;
								case 3:
									en_axis_data.x_offset = 0;
									en_axis_data.y_offset = 2;
									en_axis_data.increment = 4;
									break;
								case 4:
									en_axis_data.z_offset = 0;
									en_axis_data.increment = 2;
									break;
								case 5:
									en_axis_data.x_offset = 0;
									en_axis_data.z_offset = 2;
									en_axis_data.increment = 4;
									break;
								case 6:
									en_axis_data.y_offset = 0;
									en_axis_data.z_offset = 2;
									en_axis_data.increment = 4;
									break;
								case 7:
									en_axis_data.x_offset = 0;
									en_axis_data.y_offset = 2;
									en_axis_data.z_offset = 4;
									en_axis_data.increment = 6;
									break;
								default:
									en_axis_data.increment = 0;
							}

							var fsr_mult = .00006;
							var fsr_text = "";
							switch(globalDevices[deviceAddr].fsr){
								case 0:
									fsr_mult = 0.00006;
									break;
								case 1:
									fsr_mult = 0.00012;
									break;
								case 2:
									fsr_mult = 0.00024;
									break;
								case 3:
									fsr_mult = 0.00049;
									break;
							}
							switch(globalDevices[deviceAddr].fsr){
								case 0:
									fsr_text = "2g";
									break;
								case 1:
									fsr_text = "4g";
									break;
								case 2:
									fsr_text = "8g";
									break;
								case 3:
									fsr_text = "16g";
									break;
							}

							for(var i = 0; i < raw_data.length; i+=en_axis_data.increment){
								label++;

								fft_concat[label] = {};

								if('x_offset' in en_axis_data){
									fft_concat[label].x = parseFloat((signInt(((raw_data[i+en_axis_data.x_offset]<<8)+(raw_data[i+en_axis_data.x_offset+1])), 16)*fsr_mult).toFixed(2));
								}
								if('y_offset' in en_axis_data){
									fft_concat[label].y = parseFloat((signInt(((raw_data[i+en_axis_data.y_offset]<<8)+(raw_data[i+en_axis_data.y_offset+1])), 16)*fsr_mult).toFixed(2));
								}
								if('z_offset' in en_axis_data){
									fft_concat[label].z = parseFloat((signInt(((raw_data[i+en_axis_data.z_offset]<<8)+(raw_data[i+en_axis_data.z_offset+1])), 16)*fsr_mult).toFixed(2));
								}
							}
							var fft_concat_obj = {
								time_id: globalDevices[deviceAddr].hour +':'+ globalDevices[deviceAddr].minute,
								mac_address: deviceAddr,
								en_axis: globalDevices[deviceAddr].en_axis,
								fsr: fsr_text,
								odr: globalDevices[deviceAddr].odr,
								device_temp: globalDevices[deviceAddr].device_temp,
								data: fft_concat
							};
							sensor_data = fft_concat_obj;
							delete globalDevices[deviceAddr];
							if(this.hasOwnProperty('failure_no')){
								console.log('####falure no');
								console.log(this.failure_no);
							}

							return sensor_data;
						}
						else{
							return;
						}
					}else{

						var mode = payload[8];
						var odr = payload[9];
						var en_axis = payload[10] & 7;
						var fsr = payload[10] >> 5;
						var device_temp = signInt(msbLsb(payload[13], payload[14]))/100;
						var adc_1_raw = msbLsb(payload[15], payload[16]);
						var adc_2_raw = msbLsb(payload[17], payload[18]);


						switch(odr){
							case 6:
								odr = 50;
								break;
							case 7:
								odr = 100;
								break;
							case 8:
								odr = 200;
								break;
							case 9:
								odr = 400;
								break;
							case 10:
								odr = 800;
								break;
							case 11:
								odr = 1600;
								break;
							case 12:
								odr = 3200;
								break;
							case 13:
								odr = 6400;
								break;
							case 14:
								odr = 12800;
								break;
							case 15:
								odr = 25600;
								break;
							default:
								odr = 0;
						}

						globalDevices[deviceAddr] = {
							// stream_size: expected_packets,
							data: {},
							odr: odr,
							mo: mode,
							en_axis: en_axis,
							fsr: fsr,
							hour: hour,
							minute: minute,
							adc_1_raw: adc_1_raw,
							adc_2_raw: adc_2_raw,
							device_temp: device_temp,
						}
						globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						return;
					}
				}
				else if(payload[8] === 0 || payload[8] === 2){
					// mode byte most significant bit will indicate fft data.
					// console.log(d);
					var odr;
					switch(payload[9]){
						case 6:
							odr = "50Hz"
							break;
						case 7:
							odr = "100Hz";
							break;
						case 8:
							odr = "200Hz";
							break;
						case 9:
							odr = "400Hz";
							break;
						case 10:
							odr = "800Hz";
							break;
						case 11:
							odr = "1600Hz";
							break;
						case 12:
							odr = "3200Hz";
							break;
						case 13:
							odr = "6400Hz";
							break;
						case 14:
							odr = "12800Hz";
							break;
						case 15:
							odr = "25600Hz";
							break;
					}
					return {
						mode: payload[8],

						odr: odr,
						temperature: signInt(payload.slice(10, 12).reduce(msbLsb), 16) / 100,

						adc_1_raw: payload.slice(12, 14).reduce(msbLsb),
						adc_2_raw: payload.slice(14, 16).reduce(msbLsb),

						x_rms_ACC_G: payload.slice(16, 18).reduce(msbLsb)/1000,
						x_max_ACC_G: payload.slice(18, 20).reduce(msbLsb)/1000,
						x_velocity_mm_sec: payload.slice(20, 22).reduce(msbLsb) / 100,
						x_displacement_mm: payload.slice(22, 24).reduce(msbLsb) / 100,
						x_peak_one_Hz: payload.slice(24, 26).reduce(msbLsb),
						x_peak_two_Hz: payload.slice(26, 28).reduce(msbLsb),
						x_peak_three_Hz: payload.slice(28, 30).reduce(msbLsb),

						y_rms_ACC_G: payload.slice(30, 32).reduce(msbLsb)/1000,
						y_max_ACC_G: payload.slice(32, 34).reduce(msbLsb)/1000,
						y_velocity_mm_sec: payload.slice(34, 36).reduce(msbLsb) / 100,
						y_displacement_mm: payload.slice(36, 38).reduce(msbLsb) / 100,
						y_peak_one_Hz: payload.slice(38, 40).reduce(msbLsb),
						y_peak_two_Hz: payload.slice(40, 42).reduce(msbLsb),
						y_peak_three_Hz: payload.slice(42, 44).reduce(msbLsb),

						z_rms_ACC_G: payload.slice(44, 46).reduce(msbLsb)/1000,
						z_max_ACC_G: payload.slice(46, 48).reduce(msbLsb)/1000,
						z_velocity_mm_sec: payload.slice(48, 50).reduce(msbLsb) / 100,
						z_displacement_mm: payload.slice(50,52).reduce(msbLsb) / 100,
						z_peak_one_Hz: payload.slice(52, 54).reduce(msbLsb),
						z_peak_two_Hz: payload.slice(54, 56).reduce(msbLsb),
						z_peak_three_Hz: payload.slice(56, 58).reduce(msbLsb),
					};
				}
				// else{
				// 	parsed.data = {'error': 'Vibration mode error'}
				// 	return parsed;
				// }
			},
			'parse_fly': (frame) => {
				let frame_data = {};
				switch(frame[16]){
					case 0:
						frame_data.mode = "Processed";
						break;
					case 1:
						frame_data.mode = "Raw";
						break;
					case 2:
						frame_data.mode = "Processed + Raw on demand";
						break;
				}
				switch(frame[17]){
					case 6:
						frame_data.odr_1 = 50;
						break;
					case 7:
						frame_data.odr_1 = 100;
						break;
					case 8:
						frame_data.odr_1 = 200;
						break;
					case 9:
						frame_data.odr_1 = 400;
						break;
					case 10:
						frame_data.odr_1 = 800;
						break;
					case 11:
						frame_data.odr_1 = 1600;
						break;
					case 12:
						frame_data.odr_1 = 3200;
						break;
					case 13:
						frame_data.odr_1 = 6400;
						break;
					case 14:
						frame_data.odr_1 = 12800;
						break;
					case 15:
						frame_data.odr_1 = 25600;
						break;
				}
				frame_data.sampling_duration_1 = frame[19]*50 + "ms";
				switch(frame[21]){
					case 0:
						frame_data.filter_status = "Disabled";
						break;
					case 1:
						frame_data.filter_status = "Enabled";
						break;
				}
				switch(frame[22]){
					case 0:
						frame_data.lpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.lpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.lpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.lpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.lpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.lpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.lpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.lpf_coeff_1 = 2048;
						break;
				}
				frame_data.lpf_freq_1 = frame_data.odr_1 / frame_data.lpf_coeff_1;
				switch(frame[24]){
					case 0:
						frame_data.hpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.hpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.hpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.hpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.hpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.hpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.hpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.hpf_coeff_1 = 2048;
						break;
				}
				frame_data.hpf_freq_1 = frame_data.odr_1 / frame_data.hpf_coeff_1;
				switch(frame[26]){
					case 0:
						frame_data.sampling_interval = "5 Minutes";
						break;
					case 1:
						frame_data.sampling_interval = "10 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "15 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "20 Minutes";
						break;
					case 4:
						frame_data.sampling_interval = "30 Minutes";
						break;
					case 5:
						frame_data.sampling_interval = "60 Minutes";
						break;
					case 6:
						frame_data.sampling_interval = "120 Minutes";
						break;
					case 7:
						frame_data.sampling_interval = "180 Minutes";
						break;
					case 8:
						frame_data.sampling_interval = "1 Minute";
						break;
				}
				frame_data.on_request_timeout = frame[27] + " Seconds";
				frame_data.deadband = frame[28] + "mg";

				switch(frame[29]){
					case 0:
						frame_data.payload_length = "50 Bytes";
						break;
					case 1:
						frame_data.payload_length = "100 Bytes";
						break;
					case 2:
						frame_data.payload_length = "150 Bytes";
						break;
					case 3:
						frame_data.payload_length = "180 Bytes";
						break;
				}

				return {
					'firmware': frame[2],
					'destination_address': toMac(frame.slice(12, 16)),
					'mode': frame_data.mode,
					'odr_1': frame_data.odr_1+'Hz',
					'sampling_duration_1': frame_data.sampling_duration_1,
					'filter_status': frame_data.filter_status,
					'lpf_coeff_1': frame_data.lpf_coeff_1,
					'lpf_freq_1': frame_data.lpf_freq_1+'Hz',
					'hpf_coeff_1': frame_data.hpf_coeff_1,
					'hpf_freq_1': frame_data.hpf_freq_1+'Hz',
					'sampling_interval': frame_data.sampling_interval,
					'on_request_timeout': frame_data.on_request_timeout,
					'deadband': frame_data.deadband,
					'payload_length': frame_data.payload_length,
					'machine_values': {
						'firmware': frame[2],
						'destination_address': toMac(frame.slice(12, 16), false),
						'mode': frame[16],
						'odr_1': frame[17],
						'sampling_duration_1': frame[19],
						'filter_status': frame[21],
						'lpf_coeff_1': frame[22],
						'hpf_coeff_1': frame[24],
						'sampling_interval': frame[26],
						'on_request_timeout': frame[27],
						'deadband': frame[28],
						'payload_length': frame[29]
					}
				}
			}
		},
		'520': {
			name: 'Type 520 - 6 Channel Current Temperature and Humidity',
			parse: (payload, parsed) => {
				return {
					current_1_ma: payload.slice(0, 4).reduce(msbLsb),
					frequency_1: payload.slice(4, 6).reduce(msbLsb),
					current_2_ma: payload.slice(6, 10).reduce(msbLsb),
					frequency_2: payload.slice(10, 12).reduce(msbLsb),
					current_3_ma: payload.slice(12, 16).reduce(msbLsb),
					frequency_3: payload.slice(16, 18).reduce(msbLsb),
					current_4_ma: payload.slice(18, 22).reduce(msbLsb),
					frequency_4: payload.slice(22, 24).reduce(msbLsb),
					current_5_ma: payload.slice(24, 28).reduce(msbLsb),
					frequency_5: payload.slice(28, 30).reduce(msbLsb),
					current_6_ma: payload.slice(30, 34).reduce(msbLsb),
					frequency_6: payload.slice(34, 36).reduce(msbLsb),
					humidity: payload.slice(36, 38).reduce(msbLsb) / 100,
					temperature: signInt(payload.slice(38, 40).reduce(msbLsb), 16) / 100,
				}
			},
			'parse_fly': (frame) => {
				let frame_data = {};
				return {
					'firmware': frame[2],
					'destination_address': toMac(frame.slice(12, 16)),
					'machine_values': {
						'firmware': frame[2],
						'destination_address': toMac(frame.slice(12, 16), false),
						'frame': frame
					}
				}
			}
		},
		'521': {
			name: 'Type 521 - 3 Channel Light Sensor',
			parse: (payload, parsed) => {
				return {
					lux_1: payload.slice(0, 2).reduce(msbLsb),
					temp_1: payload.slice(2, 4).reduce(msbLsb)/100,
					ext_temp_1: payload.slice(4, 6).reduce(msbLsb)/100,
					lux_2: payload.slice(6, 8).reduce(msbLsb),
					temp_2: payload.slice(8, 10).reduce(msbLsb)/100,
					ext_temp_2: payload.slice(10, 12).reduce(msbLsb)/100,
					lux_3: payload.slice(12, 14).reduce(msbLsb),
					temp_3: payload.slice(14, 16).reduce(msbLsb)/100,
					ext_temp_3: payload.slice(16, 18).reduce(msbLsb)/100
				}
			}
		},
		'524': {
			name: 'SDI Multi Soil Probe',
			parse: (payload, parsed) => {
				return {
					moisture_1: payload.slice(0, 2).reduce(msbLsb)/100,
					temp_1: signInt(payload.slice(2, 4).reduce(msbLsb), 16)/100,
					moisture_2: payload.slice(4, 6).reduce(msbLsb)/100,
					temp_2: signInt(payload.slice(6, 8).reduce(msbLsb), 16)/100,
					moisture_3: payload.slice(8, 10).reduce(msbLsb)/100,
					temp_3: signInt(payload.slice(10, 12).reduce(msbLsb), 16)/100,
					moisture_4: payload.slice(12, 14).reduce(msbLsb)/100,
					temp_4: signInt(payload.slice(14, 16).reduce(msbLsb), 16)/100,
					moisture_5: payload.slice(16, 18).reduce(msbLsb)/100,
					temp_5: signInt(payload.slice(18, 20).reduce(msbLsb), 16)/100,
					moisture_6: payload.slice(20, 22).reduce(msbLsb)/100,
					temp_6: signInt(payload.slice(22, 24).reduce(msbLsb), 16)/100
				}
			}
		},
		'531': {
			name: 'Custom Noise Sensor',
			parse: (payload, parsed, mac) => {
				// TODO error byte is not supported at this time and the below code is utilizing a not yet implemented emitter.
				// if(payload[7] >> 1 != 0){
				// 	parsed.error = {
				// 		code: 2000080,
				// 		text: 'Sensor Probe may be unattached',
				// 		probe: 1
				// 	};
				// 	parsed.addr = mac;
				// 	parsed.data = {error: 'Error found, Sensor Probe may be unattached'};
				// 	parent._emitter.emit('ncd_error', parsed);
				// 	parent._emitter.emit('ncd_error-'+parsed.sensor_type, parsed);
				// 	parent._emitter.emit('ncd_error-'+mac, parsed);
				// 	// Delete the error so it matches older messages.
				// 	delete parsed.error;
				// 	return parsed;
				// }

				// If time series data
				if(payload[8] === 1){
					var deviceAddr = mac;
					var firmware = payload[1];
					var expected_packets = payload[10];
					var current_packet = payload[11];
					var sdata_start = 12;
					var sample_rate = payload[9];
					// console.log('current_packet');
					// console.log(current_packet);

					// Make sure its instantiated to simplify following if checks
					if(!Object.hasOwn(globalDevices, deviceAddr)){
						globalDevices[deviceAddr] = {};
					}
					// Check if new stream is from new message
					if(Object.hasOwn(globalDevices[deviceAddr], 'err_msg_counter_val') && globalDevices[deviceAddr].err_msg_counter_val != parsed.counter){
						delete globalDevices[deviceAddr].err_msg_counter_val;
					}

					// USE THESE TO TEST ERRORS
					// if(current_packet == 1){
					// 	// Test missing first packet or not having a globalDevices[deviceAddr].data
					// 	return;
					// }
					// if(current_packet == 2){
					// 	// Test missing packet mid-stream
					// 	return;
					// }
					// if(current_packet == expected_packets){
					// 	// Test missing last section of stream
					// 	return;
					// }
					// data object exists and current packet already exists in that object or if data object exists and previous packet not in data
					// This section checks if for duplicate packets already in data
					// Object.hasOwn(globalDevices, deviceAddr) && Object.hasOwn(globalDevices[deviceAddr], 'data') && current_packet in globalDevices[deviceAddr].data
					// This section checks if the previous expected packet is in the data or note
					// Object.hasOwn(globalDevices, deviceAddr) && Object.hasOwn(globalDevices[deviceAddr], 'data') && !(((current_packet&127)-1) in globalDevices[deviceAddr].data
					// This section check if data does and if current_packet is not 1. This indicates the data started mid stream
					// !Object.hasOwn(globalDevices[deviceAddr], 'data') && current_packet != 1
					if(Object.hasOwn(globalDevices[deviceAddr], 'data') && current_packet in globalDevices[deviceAddr].data || Object.hasOwn(globalDevices[deviceAddr], 'data') && !(((current_packet&127)-1) in globalDevices[deviceAddr].data) || !Object.hasOwn(globalDevices[deviceAddr], 'data') && current_packet != 1){
						// if(!Object.hasOwn(globalDevices[deviceAddr], 'err_msg_counter_val')){
						//	 globalDevices[deviceAddr].err_msg_counter_val = parsed.counter;
						// }
						// If Data exists that means we're mid packet and an error occured.
						if(Object.hasOwn(globalDevices[deviceAddr], 'data')){
							// console.log('-----');
							// console.log(deviceAddr+': bad packet breakdown deleting stream');
							// console.log(current_packet);
							// console.log(expected_packets);
							// console.log(current_packet in globalDevices[deviceAddr].data);
							// console.log(current_packet == 1);
							// console.log(!((current_packet-1) in globalDevices[deviceAddr].data));
							delete globalDevices[deviceAddr].data;
						}
						// If we have not already reported an error
						if(!Object.hasOwn(globalDevices[deviceAddr], 'err_msg_counter_val')){
							globalDevices[deviceAddr].err_msg_counter_val = parsed.counter;
							if(Object.hasOwn(globalDevices[deviceAddr], 'stream_timeout')){
								clearTimeout(globalDevices[deviceAddr].stream_timeout);
								delete globalDevices[deviceAddr].stream_timeout;
							}
							parsed.error = {
								code: 1000080,
								text: 'Faulty multi-packet stream detected',
								probe: 1
							}
							parsed.addr = mac;
							// TODO remove console with emitter
							console.log(parsed);
							// parent._emitter.emit('ncd_error', parsed);
							// parent._emitter.emit('ncd_error-'+parsed.sensor_type, parsed);
							// parent._emitter.emit('ncd_error-'+mac, parsed);
						}
					}
					// If first packet
					if(current_packet == 1){
						var mode = payload[8];

						// First packet so errors should be invalidated commented out as may not be necessary because we're redefining globalDevices
						// if(!Object.hasOwn(globalDevices[deviceAddr], 'err_msg_counter_val')){
						//	 delete globalDevices[deviceAddr].err_msg_counter_val;
						// }
						if(Object.hasOwn(globalDevices[deviceAddr], 'stream_timeout')){
							clearTimeout(globalDevices[deviceAddr].stream_timeout);
							delete globalDevices[deviceAddr].stream_timeout;
						}
						globalDevices[deviceAddr] = {
							// stream_size: expected_packets,
							data: {},
							sample_rate: sample_rate,
							mo: mode,
						}
						// This timeout is useful for the reception of the first packet, but comms cut afterward
						if(expected_packets != 1){
							globalDevices[deviceAddr].stream_timeout = setTimeout(() => {
								if(Object.hasOwn(globalDevices[deviceAddr], 'data')){
									delete globalDevices[deviceAddr].data;
								}
								parsed.error = {
									code: 1100080,
									text: 'Multi-Packet Stream started, but timed out',
									probe: 1
								}
								parsed.addr = mac;
								// TODO remove console with emitter
								console.log(parsed);
								// parent._emitter.emit('ncd_error', parsed);
								// parent._emitter.emit('ncd_error-'+parsed.sensor_type, parsed);
								// parent._emitter.emit('ncd_error-'+mac, parsed);
							}, 30000);
						};
						globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
					}else if(Object.hasOwn(globalDevices[deviceAddr], 'data')){
						// Not first packet and no error detected, append to data array
						globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
					}

					if(Object.hasOwn(globalDevices[deviceAddr], 'data') && Object.keys(globalDevices[deviceAddr].data).length == expected_packets && !Object.hasOwn(globalDevices[deviceAddr], 'err_msg_counter_val')){
						var raw_data = new Array();
						for(const packet in globalDevices[deviceAddr].data){
							raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
						}
						var label = 0;

						var data_concat = {};

						for(var i = 0; i < raw_data.length; i+=2){
							label++;

							data_concat[label] = (raw_data[i]<<8)+(raw_data[i+1]);
							// data_concat[label] = parseFloat((signInt(((raw_data[i]<<8)+(raw_data[i+1])), 16)).toFixed(3));
						}
						var data_concat_obj = {
							// mac_address: deviceAddr,
							sample_rate: globalDevices[deviceAddr].sample_rate,
							data: data_concat
						};
						sensor_data = data_concat_obj;

						// Clear stream timeout to prevent timeout error message from triggering
						if(Object.hasOwn(globalDevices[deviceAddr], 'stream_timeout')){
							clearTimeout(globalDevices[deviceAddr].stream_timeout);
						}
						delete globalDevices[deviceAddr];
						return sensor_data;
					}
					else{
						return;
					}

				}else{
					let firmware = payload[1];
					if(firmware > 1){
						return {
							mode: payload[8],
							sample_rate: payload[9],
							rms_dba: payload[10],
							c1_dba: payload[11],
							c1_freq: payload.slice(12, 14).reduce(msbLsb),
							c2_dba: payload[14],
							c2_freq: payload.slice(15, 17).reduce(msbLsb),
							c3_dba: payload[17],
							c3_freq: payload.slice(18, 20).reduce(msbLsb),
						};
					}else{
						return {
							noise_db: payload[8],
							peak_freq_1: payload.slice(9, 11).reduce(msbLsb),
							peak_freq_2: payload.slice(11, 13).reduce(msbLsb),
							peak_freq_3: payload.slice(13, 15).reduce(msbLsb)
						};
					};
				};
			},
		},
		'535': {
			name: 'Custom Wireless CO2 Gas Sensor',
			parse: (d) => {
				return {
					humidity: msbLsb(d[0], d[1])/100,
					temperature: signInt((msbLsb(d[2], d[3])), 16)/100,
					co2: msbLsb(d[4], d[5])
				};
			}
		},
		'537': {
			name: 'Type 537 - Custom Standalone Smart Vibration Sensor',
			parse: (payload, parsed, mac) => {
				if(payload[7] >> 1 != 0){
					console.log('Error found');
					parsed.data = {error: 'Error found, Sensor Probe may be unattached'};
					return parsed;
				}

				if(payload[8] === 1){
					var deviceAddr = mac;
					var firmware = payload[1];
					var hour = payload[11];
					var minute = payload[12];
					var expected_packets = payload[15];
					var current_packet = payload[16];
					var sdata_start = 17;

					if(globalDevices.hasOwnProperty(deviceAddr) || expected_packets == 1){
						if(expected_packets != 1){
							// if a packet is already stored with the same packet ID, or if packet ID is 1, or if current packet ID is not one more than last packet ID
							if(current_packet in globalDevices[deviceAddr].data || current_packet == 1 || !((current_packet-1) in globalDevices[deviceAddr].data)) {
								console.log('-----');
								console.log('bad packet breakdown deleting stream');
								console.log(current_packet);
								console.log(expected_packets);
								console.log(current_packet in globalDevices[deviceAddr].data);
								console.log(current_packet == 1);
								console.log(!((current_packet-1) in globalDevices[deviceAddr].data));
								if(this.hasOwnProperty('failure_no')){
									this.failure_no = this.failure_no + 1;
								}
								else{
									this.failure_no = 1;
								}
								if(this.hasOwnProperty('failure_no')){
									console.log('####falure no');
									console.log(this.failure_no);
								}
								// console.log(globalDevices[deviceAddr].data);
								delete globalDevices[deviceAddr];
								if(current_packet != 1){
									return;
								} else{

									var mode = payload[8];
									var odr = payload[9];
									var en_axis = payload[10] & 7;
									var fsr = payload[10] >> 5;
									var device_temp = msbLsb(payload[13], payload[14])/100;


									switch(odr){
										case 6:
											odr = 50;
											break;
										case 7:
											odr = 100;
											break;
										case 8:
											odr = 200;
											break;
										case 9:
											odr = 400;
											break;
										case 10:
											odr = 800;
											break;
										case 11:
											odr = 1600;
											break;
										case 12:
											odr = 3200;
											break;
										case 13:
											odr = 6400;
											break;
										case 14:
											odr = 12800;
											break;
										case 15:
											odr = 25600;
											break;
										default:
											odr = 0;
									}

									globalDevices[deviceAddr] = {
										// stream_size: expected_packets,
										data: {},
										odr: odr,
										mo: mode,
										en_axis: en_axis,
										fsr: fsr,
										hour: hour,
										minute: minute,
										device_temp: device_temp,
									}
									globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
									return;
								}
							}
							else{
								globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
							}
						}
						else{
							var mode = payload[8];
							var odr = payload[9];
							var en_axis = payload[10] & 7;
							var fsr = payload[10] >> 5;
							var device_temp = msbLsb(payload[13], payload[14])/100;


							switch(odr){
								case 6:
									odr = 50;
									break;
								case 7:
									odr = 100;
									break;
								case 8:
									odr = 200;
									break;
								case 9:
									odr = 400;
									break;
								case 10:
									odr = 800;
									break;
								case 11:
									odr = 1600;
									break;
								case 12:
									odr = 3200;
									break;
								case 13:
									odr = 6400;
									break;
								case 14:
									odr = 12800;
									break;
								case 15:
									odr = 25600;
									break;
								default:
									odr = 0;
							}

							globalDevices[deviceAddr] = {
								// stream_size: expected_packets,
								data: {},
								odr: odr,
								mo: mode,
								en_axis: en_axis,
								fsr: fsr,
								hour: hour,
								minute: minute,
								device_temp: device_temp,
							}
							globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						}
						if(Object.keys(globalDevices[deviceAddr].data).length == expected_packets){
							var raw_data = new Array();
							for(const packet in globalDevices[deviceAddr].data){
								raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
							}
							var label = 0;

							var fft = new Array();
							var fft_concat = {};

							var en_axis_data = {};
							switch (globalDevices[deviceAddr].en_axis){
								case 1:
									en_axis_data.x_offset = 0;
									en_axis_data.increment = 2;
									break;
								case 2:
									en_axis_data.y_offset = 0;
									en_axis_data.increment = 2;
									break;
								case 3:
									en_axis_data.x_offset = 0;
									en_axis_data.y_offset = 2;
									en_axis_data.increment = 4;
									break;
								case 4:
									en_axis_data.z_offset = 0;
									en_axis_data.increment = 2;
									break;
								case 5:
									en_axis_data.x_offset = 0;
									en_axis_data.z_offset = 2;
									en_axis_data.increment = 4;
									break;
								case 6:
									en_axis_data.y_offset = 0;
									en_axis_data.z_offset = 2;
									en_axis_data.increment = 4;
									break;
								case 7:
									en_axis_data.x_offset = 0;
									en_axis_data.y_offset = 2;
									en_axis_data.z_offset = 4;
									en_axis_data.increment = 6;
									break;
								default:
									en_axis_data.increment = 0;
							}

							var fsr_mult = .00006;
							var fsr_text = "";
							switch(globalDevices[deviceAddr].fsr){
								case 0:
									fsr_mult = 0.00006;
									break;
								case 1:
									fsr_mult = 0.00012;
									break;
								case 2:
									fsr_mult = 0.00024;
									break;
								case 3:
									fsr_mult = 0.00049;
									break;
							}
							switch(globalDevices[deviceAddr].fsr){
								case 0:
									fsr_text = "2g";
									break;
								case 1:
									fsr_text = "4g";
									break;
								case 2:
									fsr_text = "8g";
									break;
								case 3:
									fsr_text = "16g";
									break;
							}

							for(var i = 0; i < raw_data.length; i+=en_axis_data.increment){
								label++;

								fft_concat[label] = {};

								if('x_offset' in en_axis_data){
									fft_concat[label].x = parseFloat((signInt(((raw_data[i+en_axis_data.x_offset]<<8)+(raw_data[i+en_axis_data.x_offset+1])), 16)*fsr_mult).toFixed(2));
								}
								if('y_offset' in en_axis_data){
									fft_concat[label].y = parseFloat((signInt(((raw_data[i+en_axis_data.y_offset]<<8)+(raw_data[i+en_axis_data.y_offset+1])), 16)*fsr_mult).toFixed(2));
								}
								if('z_offset' in en_axis_data){
									fft_concat[label].z = parseFloat((signInt(((raw_data[i+en_axis_data.z_offset]<<8)+(raw_data[i+en_axis_data.z_offset+1])), 16)*fsr_mult).toFixed(2));
								}
							}
							var fft_concat_obj = {
								time_id: globalDevices[deviceAddr].hour +':'+ globalDevices[deviceAddr].minute,
								mac_address: deviceAddr,
								en_axis: globalDevices[deviceAddr].en_axis,
								fsr: fsr_text,
								odr: globalDevices[deviceAddr].odr,
								device_temp: globalDevices[deviceAddr].device_temp,
								data: fft_concat
							};
							sensor_data = fft_concat_obj;
							delete globalDevices[deviceAddr];
							if(this.hasOwnProperty('failure_no')){
								console.log('####falure no');
								console.log(this.failure_no);
							}

							return sensor_data;
						}
						else{
							return;
						}
					}else{

						var mode = payload[8];
						var odr = payload[9];
						var en_axis = payload[10] & 7;
						var fsr = payload[10] >> 5;
						var device_temp = msbLsb(payload[13], payload[14])/100;


						switch(odr){
							case 6:
								odr = 50;
								break;
							case 7:
								odr = 100;
								break;
							case 8:
								odr = 200;
								break;
							case 9:
								odr = 400;
								break;
							case 10:
								odr = 800;
								break;
							case 11:
								odr = 1600;
								break;
							case 12:
								odr = 3200;
								break;
							case 13:
								odr = 6400;
								break;
							case 14:
								odr = 12800;
								break;
							case 15:
								odr = 25600;
								break;
							default:
								odr = 0;
						}

						globalDevices[deviceAddr] = {
							// stream_size: expected_packets,
							data: {},
							odr: odr,
							mo: mode,
							en_axis: en_axis,
							fsr: fsr,
							hour: hour,
							minute: minute,
							device_temp: device_temp,
						}
						globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						return;
					}
				}
				else if(payload[8] === 0 || payload[8] === 2){
					// mode byte most significant bit will indicate fft data.
					// console.log(d);
					var odr;
					switch(payload[9]){
						case 6:
							odr = "50Hz"
							break;
						case 7:
							odr = "100Hz";
							break;
						case 8:
							odr = "200Hz";
							break;
						case 9:
							odr = "400Hz";
							break;
						case 10:
							odr = "800Hz";
							break;
						case 11:
							odr = "1600Hz";
							break;
						case 12:
							odr = "3200Hz";
							break;
						case 13:
							odr = "6400Hz";
							break;
						case 14:
							odr = "12800Hz";
							break;
						case 15:
							odr = "25600Hz";
							break;
					}
					return {
						mode: payload[8],

						odr: odr,
						temperature: signInt(payload.slice(10, 12).reduce(msbLsb), 16) / 100,

						x_rms_ACC_G: payload.slice(12, 14).reduce(msbLsb)/1000,
						x_max_ACC_G: payload.slice(14, 16).reduce(msbLsb)/1000,
						x_velocity_mm_sec: payload.slice(16, 18).reduce(msbLsb) / 100,
						x_displacement_mm: payload.slice(18, 20).reduce(msbLsb) / 100,
						x_peak_one_Hz: payload.slice(20, 22).reduce(msbLsb),
						x_peak_two_Hz: payload.slice(22, 24).reduce(msbLsb),
						x_peak_three_Hz: payload.slice(24, 26).reduce(msbLsb),

						y_rms_ACC_G: payload.slice(26, 28).reduce(msbLsb)/1000,
						y_max_ACC_G: payload.slice(28, 30).reduce(msbLsb)/1000,
						y_velocity_mm_sec: payload.slice(30, 32).reduce(msbLsb) / 100,
						y_displacement_mm: payload.slice(32, 34).reduce(msbLsb) / 100,
						y_peak_one_Hz: payload.slice(34, 36).reduce(msbLsb),
						y_peak_two_Hz: payload.slice(36, 38).reduce(msbLsb),
						y_peak_three_Hz: payload.slice(38, 40).reduce(msbLsb),

						z_rms_ACC_G: payload.slice(40, 42).reduce(msbLsb)/1000,
						z_max_ACC_G: payload.slice(42, 44).reduce(msbLsb)/1000,
						z_velocity_mm_sec: payload.slice(44, 46).reduce(msbLsb) / 100,
						z_displacement_mm: payload.slice(46, 48).reduce(msbLsb) / 100,
						z_peak_one_Hz: payload.slice(48, 50).reduce(msbLsb),
						z_peak_two_Hz: payload.slice(50, 52).reduce(msbLsb),
						z_peak_three_Hz: payload.slice(52, 54).reduce(msbLsb),
					};
				}
				// else{
				// 	parsed.data = {'error': 'Vibration mode error'}
				// 	return parsed;
				// }
			},
			'parse_fly': (frame) => {
				let frame_data = {};
				switch(frame[16]){
					case 0:
						frame_data.mode = "Processed";
						break;
					case 1:
						frame_data.mode = "Raw";
						break;
					case 2:
						frame_data.mode = "Processed + Raw on demand";
						break;
				}
				switch(frame[17]){
					case 6:
						frame_data.odr_1 = 50;
						break;
					case 7:
						frame_data.odr_1 = 100;
						break;
					case 8:
						frame_data.odr_1 = 200;
						break;
					case 9:
						frame_data.odr_1 = 400;
						break;
					case 10:
						frame_data.odr_1 = 800;
						break;
					case 11:
						frame_data.odr_1 = 1600;
						break;
					case 12:
						frame_data.odr_1 = 3200;
						break;
					case 13:
						frame_data.odr_1 = 6400;
						break;
					case 14:
						frame_data.odr_1 = 12800;
						break;
					case 15:
						frame_data.odr_1 = 25600;
						break;
				}
				frame_data.sampling_duration_1 = frame[19]*50 + "ms";
				switch(frame[21]){
					case 0:
						frame_data.filter_status = "Disabled";
						break;
					case 1:
						frame_data.filter_status = "Enabled";
						break;
				}
				switch(frame[22]){
					case 0:
						frame_data.lpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.lpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.lpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.lpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.lpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.lpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.lpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.lpf_coeff_1 = 2048;
						break;
				}
				frame_data.lpf_freq_1 = frame_data.odr_1 / frame_data.lpf_coeff_1;
				switch(frame[24]){
					case 0:
						frame_data.hpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.hpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.hpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.hpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.hpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.hpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.hpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.hpf_coeff_1 = 2048;
						break;
				}
				frame_data.hpf_freq_1 = frame_data.odr_1 / frame_data.hpf_coeff_1;
				switch(frame[26]){
					case 0:
						frame_data.sampling_interval = "5 Minutes";
						break;
					case 1:
						frame_data.sampling_interval = "10 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "15 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "20 Minutes";
						break;
					case 4:
						frame_data.sampling_interval = "30 Minutes";
						break;
					case 5:
						frame_data.sampling_interval = "60 Minutes";
						break;
					case 6:
						frame_data.sampling_interval = "120 Minutes";
						break;
					case 7:
						frame_data.sampling_interval = "180 Minutes";
						break;
					case 8:
						frame_data.sampling_interval = "1 Minute";
						break;
				}
				frame_data.on_request_timeout = frame[27] + " Seconds";
				frame_data.deadband = frame[28] + "mg";

				switch(frame[29]){
					case 0:
						frame_data.payload_length = "50 Bytes";
						break;
					case 1:
						frame_data.payload_length = "100 Bytes";
						break;
					case 2:
						frame_data.payload_length = "150 Bytes";
						break;
					case 3:
						frame_data.payload_length = "180 Bytes";
						break;
				}

				return {
					'firmware': frame[2],
					'destination_address': toMac(frame.slice(12, 16)),
					'mode': frame_data.mode,
					'odr_1': frame_data.odr_1+'Hz',
					'sampling_duration_1': frame_data.sampling_duration_1,
					'filter_status': frame_data.filter_status,
					'lpf_coeff_1': frame_data.lpf_coeff_1,
					'lpf_freq_1': frame_data.lpf_freq_1+'Hz',
					'hpf_coeff_1': frame_data.hpf_coeff_1,
					'hpf_freq_1': frame_data.hpf_freq_1+'Hz',
					'sampling_interval': frame_data.sampling_interval,
					'on_request_timeout': frame_data.on_request_timeout,
					'deadband': frame_data.deadband,
					'payload_length': frame_data.payload_length,
					'machine_values': {
						'firmware': frame[2],
						'destination_address': toMac(frame.slice(12, 16), false),
						'mode': frame[16],
						'odr_1': frame[17],
						'sampling_duration_1': frame[19],
						'filter_status': frame[21],
						'lpf_coeff_1': frame[22],
						'hpf_coeff_1': frame[24],
						'sampling_interval': frame[26],
						'on_request_timeout': frame[27],
						'deadband': frame[28],
						'payload_length': frame[29]
					}
				}
			}
		},
		'538': {
			name: 'One Channel Vibration Plus',
			parse: (payload, parsed, mac) => {
				if(payload[7] >> 1 != 0){
					console.log('Error found');
					parsed.data = {error: 'Error found, Sensor Probe may be unattached'};
					return parsed;
				}

				if(payload[8] === 1){
					var deviceAddr = mac;
					var firmware = payload[1];
					var hour = payload[11];
					var minute = payload[12];
					var expected_packets = payload[15];
					var current_packet = payload[16];
					var sdata_start = 17;

					if(globalDevices.hasOwnProperty(deviceAddr) || expected_packets == 1){
						if(expected_packets != 1){
							// if a packet is already stored with the same packet ID, or if packet ID is 1, or if current packet ID is not one more than last packet ID
							if(current_packet in globalDevices[deviceAddr].data || current_packet == 1 || !(((current_packet&127)-1) in globalDevices[deviceAddr].data)) {
								console.log('-----');
								console.log('bad packet breakdown deleting stream');
								console.log(current_packet);
								console.log(expected_packets);
								console.log(current_packet in globalDevices[deviceAddr].data);
								console.log(current_packet == 1);
								console.log(!((current_packet-1) in globalDevices[deviceAddr].data));
								if(this.hasOwnProperty('failure_no')){
									this.failure_no = this.failure_no + 1;
								}
								else{
									this.failure_no = 1;
								}
								if(this.hasOwnProperty('failure_no')){
									console.log('####falure no');
									console.log(this.failure_no);
								}
								// console.log(globalDevices[deviceAddr].data);
								delete globalDevices[deviceAddr];
								if(current_packet != 1){
									return;
								} else{

									var mode = payload[8];
									var odr = payload[9];
									var en_axis = payload[10] & 7;
									var fsr = payload[10] >> 5;
									var device_temp = msbLsb(payload[13], payload[14])/100;


									switch(odr){
										case 6:
											odr = 50;
											break;
										case 7:
											odr = 100;
											break;
										case 8:
											odr = 200;
											break;
										case 9:
											odr = 400;
											break;
										case 10:
											odr = 800;
											break;
										case 11:
											odr = 1600;
											break;
										case 12:
											odr = 3200;
											break;
										case 13:
											odr = 6400;
											break;
										case 14:
											odr = 12800;
											break;
										case 15:
											odr = 25600;
											break;
										default:
											odr = 0;
									}

									globalDevices[deviceAddr] = {
										// stream_size: expected_packets,
										data: {},
										odr: odr,
										mo: mode,
										en_axis: en_axis,
										fsr: fsr,
										hour: hour,
										minute: minute,
										device_temp: device_temp,
									}
									globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
									return;
								}
							}
							else{
								globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
							}
						}
						else{
							var mode = payload[8];
							var odr = payload[9];
							var en_axis = payload[10] & 7;
							var fsr = payload[10] >> 5;
							var device_temp = msbLsb(payload[13], payload[14])/100;


							switch(odr){
								case 6:
									odr = 50;
									break;
								case 7:
									odr = 100;
									break;
								case 8:
									odr = 200;
									break;
								case 9:
									odr = 400;
									break;
								case 10:
									odr = 800;
									break;
								case 11:
									odr = 1600;
									break;
								case 12:
									odr = 3200;
									break;
								case 13:
									odr = 6400;
									break;
								case 14:
									odr = 12800;
									break;
								case 15:
									odr = 25600;
									break;
								default:
									odr = 0;
							}

							globalDevices[deviceAddr] = {
								// stream_size: expected_packets,
								data: {},
								odr: odr,
								mo: mode,
								en_axis: en_axis,
								fsr: fsr,
								hour: hour,
								minute: minute,
								device_temp: device_temp,
							}
							globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						}

						if(Object.keys(globalDevices[deviceAddr].data).length == expected_packets){
							var raw_data = new Array();
							for(const packet in globalDevices[deviceAddr].data){
								raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
							}
							var label = 0;

							var fft = new Array();
							var fft_concat = {};

							var en_axis_data = {};
							switch (globalDevices[deviceAddr].en_axis){
								case 1:
									en_axis_data.x_offset = 0;
									en_axis_data.increment = 2;
									break;
								case 2:
									en_axis_data.y_offset = 0;
									en_axis_data.increment = 2;
									break;
								case 3:
									en_axis_data.x_offset = 0;
									en_axis_data.y_offset = 2;
									en_axis_data.increment = 4;
									break;
								case 4:
									en_axis_data.z_offset = 0;
									en_axis_data.increment = 2;
									break;
								case 5:
									en_axis_data.x_offset = 0;
									en_axis_data.z_offset = 2;
									en_axis_data.increment = 4;
									break;
								case 6:
									en_axis_data.y_offset = 0;
									en_axis_data.z_offset = 2;
									en_axis_data.increment = 4;
									break;
								case 7:
									en_axis_data.x_offset = 0;
									en_axis_data.y_offset = 2;
									en_axis_data.z_offset = 4;
									en_axis_data.increment = 6;
									break;
								default:
									en_axis_data.increment = 0;
							}

							var fsr_mult = .00006;
							var fsr_text = "";
							switch(globalDevices[deviceAddr].fsr){
								case 0:
									fsr_mult = 0.00006;
									break;
								case 1:
									fsr_mult = 0.00012;
									break;
								case 2:
									fsr_mult = 0.00024;
									break;
								case 3:
									fsr_mult = 0.00049;
									break;
							}
							switch(globalDevices[deviceAddr].fsr){
								case 0:
									fsr_text = "2g";
									break;
								case 1:
									fsr_text = "4g";
									break;
								case 2:
									fsr_text = "8g";
									break;
								case 3:
									fsr_text = "16g";
									break;
							}

							for(var i = 0; i < raw_data.length; i+=en_axis_data.increment){
								label++;

								fft_concat[label] = {};

								if('x_offset' in en_axis_data){
									fft_concat[label].x = parseFloat((signInt(((raw_data[i+en_axis_data.x_offset]<<8)+(raw_data[i+en_axis_data.x_offset+1])), 16)*fsr_mult).toFixed(2));
								}
								if('y_offset' in en_axis_data){
									fft_concat[label].y = parseFloat((signInt(((raw_data[i+en_axis_data.y_offset]<<8)+(raw_data[i+en_axis_data.y_offset+1])), 16)*fsr_mult).toFixed(2));
								}
								if('z_offset' in en_axis_data){
									fft_concat[label].z = parseFloat((signInt(((raw_data[i+en_axis_data.z_offset]<<8)+(raw_data[i+en_axis_data.z_offset+1])), 16)*fsr_mult).toFixed(2));
								}
							}
							var fft_concat_obj = {
								time_id: globalDevices[deviceAddr].hour +':'+ globalDevices[deviceAddr].minute,
								mac_address: deviceAddr,
								en_axis: globalDevices[deviceAddr].en_axis,
								fsr: fsr_text,
								odr: globalDevices[deviceAddr].odr,
								device_temp: globalDevices[deviceAddr].device_temp,
								data: fft_concat
							};
							sensor_data = fft_concat_obj;
							delete globalDevices[deviceAddr];
							if(this.hasOwnProperty('failure_no')){
								console.log('####falure no');
								console.log(this.failure_no);
							}

							return sensor_data;
						}
						else{
							return;
						}
					}else{

						var mode = payload[8];
						var odr = payload[9];
						var en_axis = payload[10] & 7;
						var fsr = payload[10] >> 5;
						var device_temp = msbLsb(payload[13], payload[14])/100;


						switch(odr){
							case 6:
								odr = 50;
								break;
							case 7:
								odr = 100;
								break;
							case 8:
								odr = 200;
								break;
							case 9:
								odr = 400;
								break;
							case 10:
								odr = 800;
								break;
							case 11:
								odr = 1600;
								break;
							case 12:
								odr = 3200;
								break;
							case 13:
								odr = 6400;
								break;
							case 14:
								odr = 12800;
								break;
							case 15:
								odr = 25600;
								break;
							default:
								odr = 0;
						}

						globalDevices[deviceAddr] = {
							// stream_size: expected_packets,
							data: {},
							odr: odr,
							mo: mode,
							en_axis: en_axis,
							fsr: fsr,
							hour: hour,
							minute: minute,
							device_temp: device_temp,
						}
						globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						return;
					}
				}
				else if(payload[8] === 0 || payload[8] === 2){
					// mode byte most significant bit will indicate fft data.
					// console.log(d);
					var odr;
					switch(payload[9]){
						case 6:
							odr = "50Hz"
							break;
						case 7:
							odr = "100Hz";
							break;
						case 8:
							odr = "200Hz";
							break;
						case 9:
							odr = "400Hz";
							break;
						case 10:
							odr = "800Hz";
							break;
						case 11:
							odr = "1600Hz";
							break;
						case 12:
							odr = "3200Hz";
							break;
						case 13:
							odr = "6400Hz";
							break;
						case 14:
							odr = "12800Hz";
							break;
						case 15:
							odr = "25600Hz";
							break;
					}
					return {
						mode: payload[8],

						odr: odr,
						temperature: signInt(payload.slice(10, 12).reduce(msbLsb), 16) / 100,

						x_rms_ACC_G: payload.slice(12, 14).reduce(msbLsb)/1000,
						x_max_ACC_G: payload.slice(14, 16).reduce(msbLsb)/1000,
						x_velocity_mm_sec: payload.slice(16, 18).reduce(msbLsb) / 100,
						x_displacement_mm: payload.slice(18, 20).reduce(msbLsb) / 100,
						x_peak_one_Hz: payload.slice(20, 22).reduce(msbLsb),
						x_peak_two_Hz: payload.slice(22, 24).reduce(msbLsb),
						x_peak_three_Hz: payload.slice(24, 26).reduce(msbLsb),

						y_rms_ACC_G: payload.slice(26, 28).reduce(msbLsb)/1000,
						y_max_ACC_G: payload.slice(28, 30).reduce(msbLsb)/1000,
						y_velocity_mm_sec: payload.slice(30, 32).reduce(msbLsb) / 100,
						y_displacement_mm: payload.slice(32, 34).reduce(msbLsb) / 100,
						y_peak_one_Hz: payload.slice(34, 36).reduce(msbLsb),
						y_peak_two_Hz: payload.slice(36, 38).reduce(msbLsb),
						y_peak_three_Hz: payload.slice(38, 40).reduce(msbLsb),

						z_rms_ACC_G: payload.slice(40, 42).reduce(msbLsb)/1000,
						z_max_ACC_G: payload.slice(42, 44).reduce(msbLsb)/1000,
						z_velocity_mm_sec: payload.slice(44, 46).reduce(msbLsb) / 100,
						z_displacement_mm: payload.slice(46, 48).reduce(msbLsb) / 100,
						z_peak_one_Hz: payload.slice(48, 50).reduce(msbLsb),
						z_peak_two_Hz: payload.slice(50, 52).reduce(msbLsb),
						z_peak_three_Hz: payload.slice(52, 54).reduce(msbLsb),
					};
				}
				// else{
				// 	parsed.data = {'error': 'Vibration mode error'}
				// 	return parsed;
				// }
			},
			'parse_fly': (frame) => {
				let frame_data = {};
				switch(frame[16]){
					case 0:
						frame_data.mode = "Processed";
						break;
					case 1:
						frame_data.mode = "Raw";
						break;
					case 2:
						frame_data.mode = "Processed + Raw on demand";
						break;
				}
				switch(frame[17]){
					case 6:
						frame_data.odr_1 = 50;
						break;
					case 7:
						frame_data.odr_1 = 100;
						break;
					case 8:
						frame_data.odr_1 = 200;
						break;
					case 9:
						frame_data.odr_1 = 400;
						break;
					case 10:
						frame_data.odr_1 = 800;
						break;
					case 11:
						frame_data.odr_1 = 1600;
						break;
					case 12:
						frame_data.odr_1 = 3200;
						break;
					case 13:
						frame_data.odr_1 = 6400;
						break;
					case 14:
						frame_data.odr_1 = 12800;
						break;
					case 15:
						frame_data.odr_1 = 25600;
						break;
				}
				frame_data.sampling_duration_1 = frame[19]*50 + "ms";
				switch(frame[21]){
					case 0:
						frame_data.filter_status = "Disabled";
						break;
					case 1:
						frame_data.filter_status = "Enabled";
						break;
				}
				switch(frame[22]){
					case 0:
						frame_data.lpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.lpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.lpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.lpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.lpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.lpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.lpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.lpf_coeff_1 = 2048;
						break;
				}
				frame_data.lpf_freq_1 = frame_data.odr_1 / frame_data.lpf_coeff_1;
				switch(frame[24]){
					case 0:
						frame_data.hpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.hpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.hpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.hpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.hpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.hpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.hpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.hpf_coeff_1 = 2048;
						break;
				}
				frame_data.hpf_freq_1 = frame_data.odr_1 / frame_data.hpf_coeff_1;
				switch(frame[26]){
					case 0:
						frame_data.sampling_interval = "5 Minutes";
						break;
					case 1:
						frame_data.sampling_interval = "10 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "15 Minutes";
						break;
					case 2:
						frame_data.sampling_interval = "20 Minutes";
						break;
					case 4:
						frame_data.sampling_interval = "30 Minutes";
						break;
					case 5:
						frame_data.sampling_interval = "60 Minutes";
						break;
					case 6:
						frame_data.sampling_interval = "120 Minutes";
						break;
					case 7:
						frame_data.sampling_interval = "180 Minutes";
						break;
					case 8:
						frame_data.sampling_interval = "1 Minute";
						break;
				}
				frame_data.on_request_timeout = frame[27] + " Seconds";
				frame_data.deadband = frame[28] + "mg";

				switch(frame[29]){
					case 0:
						frame_data.payload_length = "50 Bytes";
						break;
					case 1:
						frame_data.payload_length = "100 Bytes";
						break;
					case 2:
						frame_data.payload_length = "150 Bytes";
						break;
					case 3:
						frame_data.payload_length = "180 Bytes";
						break;
				}

				return {
					'firmware': frame[2],
					'destination_address': toMac(frame.slice(12, 16)),
					'mode': frame_data.mode,
					'odr_1': frame_data.odr_1+'Hz',
					'sampling_duration_1': frame_data.sampling_duration_1,
					'filter_status': frame_data.filter_status,
					'lpf_coeff_1': frame_data.lpf_coeff_1,
					'lpf_freq_1': frame_data.lpf_freq_1+'Hz',
					'hpf_coeff_1': frame_data.hpf_coeff_1,
					'hpf_freq_1': frame_data.hpf_freq_1+'Hz',
					'sampling_interval': frame_data.sampling_interval,
					'on_request_timeout': frame_data.on_request_timeout,
					'deadband': frame_data.deadband,
					'payload_length': frame_data.payload_length,
					'machine_values': {
						'firmware': frame[2],
						'destination_address': toMac(frame.slice(12, 16), false),
						'mode': frame[16],
						'odr_1': frame[17],
						'sampling_duration_1': frame[19],
						'filter_status': frame[21],
						'lpf_coeff_1': frame[22],
						'hpf_coeff_1': frame[24],
						'sampling_interval': frame[26],
						'on_request_timeout': frame[27],
						'deadband': frame[28],
						'payload_length': frame[29]
					}
				}
			}
		},
		'542': {
			name: 'Custom LWGY Flow Sensor',
			parse: (d) => {
				return {
					frequency: d.slice(0, 2).reduce(msbLsb),
					vol_flow: d.slice(2, 4).reduce(msbLsb)/100
				};
			},
			'parse_fly': (frame) => {
				return {
					'firmware': frame[2],
					'hardware_id': frame.slice(12, 15),
					'report_rate': frame.slice(15, 19).reduce(msbLsb) + " sec",
					'tx_life_counter': frame.slice(19, 23).reduce(msbLsb),
					'machine_values': {
						'firmware': frame[2],
						'hardware_id': frame.slice(12, 15),
						'report_rate': frame.slice(15, 19),
						'tx_life_counter': frame.slice(19, 23)
					}
				}
			}
		},
		'543': {
			name: 'Custom Seismic Vibration Sensor',
			parse: (payload, parsed, mac) => {
				if(payload[7] & 2){
					console.log('Error found');
					parsed.data = {error: 'Error found, Sensor Probe may be unattached'};
					return parsed;
				}
				let msg_type = 'regular';
				if(payload[8] === 1){
					var deviceAddr = mac;
					var firmware = payload[1];
					var hour = payload[12];
					var minute = payload[13];
					var expected_packets = msbLsb(payload[16], payload[17]);
					var current_packet = msbLsb(payload[18], payload[19]);
					var sdata_start = 20;

					if(globalDevices.hasOwnProperty(deviceAddr) || expected_packets == 1){
						if(expected_packets != 1){
							// if a packet is already stored with the same packet ID, or if packet ID is 1, or if current packet ID is not one more than last packet ID
							if(current_packet in globalDevices[deviceAddr].data || current_packet == 1 || !(((current_packet)-1) in globalDevices[deviceAddr].data)) {
								console.log('-----');
								console.log('bad packet breakdown deleting stream');
								console.log(current_packet);
								console.log(expected_packets);
								console.log(current_packet in globalDevices[deviceAddr].data);
								console.log(current_packet == 1);
								console.log(!((current_packet-1) in globalDevices[deviceAddr].data));
								if(this.hasOwnProperty('failure_no')){
									this.failure_no = this.failure_no + 1;
								}
								else{
									this.failure_no = 1;
								}
								if(this.hasOwnProperty('failure_no')){
									console.log('####falure no');
									console.log(this.failure_no);
								}
								// console.log(globalDevices[deviceAddr].data);
								delete globalDevices[deviceAddr];
								if(current_packet != 1){
									return;
								} else{

									var mode = payload[8];
									var odr = msbLsb(payload[9], payload[10]);
									var fsr = payload[11] >> 5;
									var temperature = msbLsb(payload[14], payload[15])/100;

									globalDevices[deviceAddr] = {
										// stream_size: expected_packets,
										data: {},
										odr: odr,
										mo: mode,
										fsr: fsr,
										hour: hour,
										minute: minute,
										temperature: temperature,
									}
									globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
									return;
								}
							}
							else{
								globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
							}
						}
						else{
							var mode = payload[8];
							var odr = msbLsb(payload[9], payload[10]);
							var fsr = payload[11] >> 5;
							var temperature = msbLsb(payload[14], payload[15])/100;

							globalDevices[deviceAddr] = {
								// stream_size: expected_packets,
								data: {},
								odr: odr,
								mo: mode,
								fsr: fsr,
								hour: hour,
								minute: minute,
								temperature: temperature,
							}
							globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						}

						if(Object.keys(globalDevices[deviceAddr].data).length == expected_packets){
							var raw_data = new Array();
							for(const packet in globalDevices[deviceAddr].data){
								raw_data = raw_data.concat(globalDevices[deviceAddr].data[packet]);
							}
							var label = 0;

							var fft = new Array();
							var fft_concat = {};

							var en_axis_data = {};
							en_axis_data.x_offset = 0;
							en_axis_data.y_offset = 2;
							en_axis_data.z_offset = 4;
							en_axis_data.increment = 6;
							fft_concat = {x: [], y: [], z: []};

							var fsr_mult = .00006;
							var fsr_text = "";
							switch(globalDevices[deviceAddr].fsr){
								case 0:
									fsr_mult = 0.0000608;
									break;
								case 1:
									fsr_mult = 0.0001216;
									break;
								case 2:
									fsr_mult = 0.0002448;
									break;
							}
							switch(globalDevices[deviceAddr].fsr){
								case 0:
									fsr_text = "2g";
									break;
								case 1:
									fsr_text = "4g";
									break;
								case 2:
									fsr_text = "8g";
									break;
							}


							for(var i = 0; i < raw_data.length; i+=en_axis_data.increment){
								label++;

								if('x_offset' in en_axis_data){
									fft_concat.x.push(parseFloat((signInt(((raw_data[i+en_axis_data.x_offset]<<8)+(raw_data[i+en_axis_data.x_offset+1])), 16)*fsr_mult).toFixed(2)));
								}
								if('y_offset' in en_axis_data){
									fft_concat.y.push(parseFloat((signInt(((raw_data[i+en_axis_data.y_offset]<<8)+(raw_data[i+en_axis_data.y_offset+1])), 16)*fsr_mult).toFixed(2)));
								}
								if('z_offset' in en_axis_data){
									fft_concat.z.push(parseFloat((signInt(((raw_data[i+en_axis_data.z_offset]<<8)+(raw_data[i+en_axis_data.z_offset+1])), 16)*fsr_mult).toFixed(2)));
								}
							}
							var fft_concat_obj = {
								mode: payload[8],
								msg_type: msg_type,
								time_id: [
									String(globalDevices[deviceAddr].hour).padStart(2, '0'),
									String(globalDevices[deviceAddr].minute).padStart(2, '0'),
								].join(':'),
								mac_address: deviceAddr,
								fsr: fsr_text,
								odr: globalDevices[deviceAddr].odr,
								temperature: globalDevices[deviceAddr].temperature,
								total_samples: label,
								data: fft_concat
							};
							sensor_data = fft_concat_obj;
							delete globalDevices[deviceAddr];
							if(this.hasOwnProperty('failure_no')){
								console.log('####falure no');
								console.log(this.failure_no);
							}

							return sensor_data;
						}
						else{
							return;
						}
					}else{

						var mode = payload[8];
						var odr = msbLsb(payload[9], payload[10]);
						var fsr = payload[11] >> 5;
						var temperature = msbLsb(payload[14], payload[15])/100;

						globalDevices[deviceAddr] = {
							// stream_size: expected_packets,
							data: {},
							odr: odr,
							mo: mode,
							fsr: fsr,
							hour: hour,
							minute: minute,
							temperature: temperature,
						}
						globalDevices[deviceAddr].data[current_packet] = payload.slice(sdata_start);
						return;
					}
				}
				else if(payload[8] === 0 || payload[8] === 2 || payload[8] === 3){
					// mode byte most significant bit will indicate fft data.
					// console.log(d);
					var odr;
					switch(payload[9]){
						case 3:
							odr = "32.25Hz"
							break;
						case 4:
							odr = "62.5Hz";
							break;
						case 5:
							odr = "125Hz";
							break;
						case 6:
							odr = "250Hz";
							break;
						case 7:
							odr = "500Hz";
							break;
					}
					return {
						mode: payload[8],
						msg_type: msg_type,

						odr: odr,
						temperature: signInt(payload.slice(10, 12).reduce(msbLsb), 16) / 100,

						x_rms_ACC_G: payload.slice(12, 14).reduce(msbLsb)/1000,
						x_max_ACC_G: payload.slice(14, 16).reduce(msbLsb)/1000,
						x_velocity_mm_sec: payload.slice(16, 18).reduce(msbLsb) / 100,
						x_displacement_mm: payload.slice(18, 20).reduce(msbLsb) / 100,
						x_peak_one_Hz: payload.slice(20, 22).reduce(msbLsb)/100,
						x_peak_two_Hz: payload.slice(22, 24).reduce(msbLsb)/100,
						x_peak_three_Hz: payload.slice(24, 26).reduce(msbLsb)/100,

						y_rms_ACC_G: payload.slice(26, 28).reduce(msbLsb)/1000,
						y_max_ACC_G: payload.slice(28, 30).reduce(msbLsb)/1000,
						y_velocity_mm_sec: payload.slice(30, 32).reduce(msbLsb) / 100,
						y_displacement_mm: payload.slice(32, 34).reduce(msbLsb) / 100,
						y_peak_one_Hz: payload.slice(34, 36).reduce(msbLsb)/100,
						y_peak_two_Hz: payload.slice(36, 38).reduce(msbLsb)/100,
						y_peak_three_Hz: payload.slice(38, 40).reduce(msbLsb)/100,

						z_rms_ACC_G: payload.slice(40, 42).reduce(msbLsb)/1000,
						z_max_ACC_G: payload.slice(42, 44).reduce(msbLsb)/1000,
						z_velocity_mm_sec: payload.slice(44, 46).reduce(msbLsb) / 100,
						z_displacement_mm: payload.slice(46, 48).reduce(msbLsb) / 100,
						z_peak_one_Hz: payload.slice(48, 50).reduce(msbLsb)/100,
						z_peak_two_Hz: payload.slice(50, 52).reduce(msbLsb)/100,
						z_peak_three_Hz: payload.slice(52, 54).reduce(msbLsb)/100
					};
				}
				// else{
				// 	parsed.data = {'error': 'Vibration mode error'}
				// 	return parsed;
				// }
			},
			'parse_fly': (frame) => {
				let frame_data = {};
				switch(frame[16]){
					case 0:
						frame_data.mode = "Processed";
						break;
					case 1:
						frame_data.mode = "Raw";
						break;
					case 2:
						frame_data.mode = "Processed + Raw on demand";
						break;
					case 3:
						frame_data.mode = "Smart";
						break;
				}
				switch(frame[17]){
					case 3:
						frame_data.odr_1 = 31.25;
						break;
					case 4:
						frame_data.odr_1 = 62.5;
						break;
					case 5:
						frame_data.odr_1 = 125;
						break;
					case 6:
						frame_data.odr_1 = 250;
						break;
					case 7:
						frame_data.odr_1 = 500;
						break;
				}
				frame_data.sampling_duration_1 = frame[18] + "sec";
				switch(frame[19]){
					case 0:
						frame_data.filter_status = "Disabled";
						break;
					case 1:
						frame_data.filter_status = "Enabled";
						break;
				}
				switch(frame[20]){
					case 0:
						frame_data.lpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.lpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.lpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.lpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.lpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.lpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.lpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.lpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.lpf_coeff_1 = 2048;
						break;
				}
				frame_data.lpf_freq_1 = frame_data.odr_1 / frame_data.lpf_coeff_1;
				switch(frame[21]){
					case 0:
						frame_data.hpf_coeff_1 = 4;
						break;
					case 1:
						frame_data.hpf_coeff_1 = 8;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 16;
						break;
					case 2:
						frame_data.hpf_coeff_1 = 32;
						break;
					case 4:
						frame_data.hpf_coeff_1 = 64;
						break;
					case 5:
						frame_data.hpf_coeff_1 = 128;
						break;
					case 6:
						frame_data.hpf_coeff_1 = 256;
						break;
					case 7:
						frame_data.hpf_coeff_1 = 512;
						break;
					case 8:
						frame_data.hpf_coeff_1 = 1024;
						break;
					case 9:
						frame_data.hpf_coeff_1 = 2048;
						break;
				}
				frame_data.hpf_freq_1 = (frame_data.odr_1 / frame_data.hpf_coeff_1).toFixed(4);
				switch(frame[22]){
					case 0:
						frame_data.sampling_interval = "5 Minutes";
						frame_data.sampling_interval_number = 5;
						break;
					case 1:
						frame_data.sampling_interval = "10 Minutes";
						frame_data.sampling_interval_number = 10;
						break;
					case 2:
						frame_data.sampling_interval = "15 Minutes";
						frame_data.sampling_interval_number = 15;
						break;
					case 2:
						frame_data.sampling_interval = "20 Minutes";
						frame_data.sampling_interval_number = 20;
						break;
					case 4:
						frame_data.sampling_interval = "30 Minutes";
						frame_data.sampling_interval_number = 30;
						break;
					case 5:
						frame_data.sampling_interval = "60 Minutes";
						frame_data.sampling_interval_number = 60;
						break;
					case 6:
						frame_data.sampling_interval = "120 Minutes";
						frame_data.sampling_interval_number = 120;
						break;
					case 7:
						frame_data.sampling_interval = "180 Minutes";
						frame_data.sampling_interval_number = 180;
						break;
					case 8:
						frame_data.sampling_interval = "1 Minute";
						frame_data.sampling_interval_number = 1;
						break;
				}
				frame_data.on_request_timeout = frame[23] + " Seconds";
				frame_data.deadband = frame[24] + "mg";

				switch(frame[25]){
					case 0:
						frame_data.payload_length = "50 Bytes";
						break;
					case 1:
						frame_data.payload_length = "100 Bytes";
						break;
					case 2:
						frame_data.payload_length = "150 Bytes";
						break;
					case 3:
						frame_data.payload_length = "180 Bytes";
						break;
				}
				switch(frame[26]){
					case 0:
						frame_data.fsr_text = "2g";
						break;
					case 1:
						frame_data.fsr_text = "4g";
						break;
					case 2:
						frame_data.fsr_text = "8g";
						break;
				}

				frame_data.auto_raw_interval = frame[31] * frame_data.sampling_interval_number || 'disabled';
				frame_data.auto_raw_interval = typeof frame_data.auto_raw_interval === 'number' ? frame_data.auto_raw_interval+'min' : frame_data.auto_raw_interval;
				frame_data.smart_mode_threshold = frame[33] * 50;
				frame_data.motion_to_delay = frame[41] * 50;

				return {
					'firmware': frame[2],
					'destination_address': toMac(frame.slice(12, 16)),
					'mode': frame_data.mode,
					'odr': frame_data.odr_1+'Hz',
					'sampling_duration': frame_data.sampling_duration_1,
					'filter_status': frame_data.filter_status,
					'lpf_coeff': frame_data.lpf_coeff_1,
					'lpf_freq': frame_data.lpf_freq_1+'Hz',
					'hpf_coeff': frame_data.hpf_coeff_1,
					'hpf_freq': frame_data.hpf_freq_1+'Hz',
					'sampling_interval': frame_data.sampling_interval,
					'on_request_timeout': frame_data.on_request_timeout,
					'deadband': frame_data.deadband,
					'payload_length': frame_data.payload_length,
					'fsr': frame_data.fsr_text,
					'auto_raw_destination_address': toMac(frame.slice(27 , 31)),
					'auto_raw_interval': frame_data.auto_raw_interval,
					'smart_mode_skip_count': frame[32],
					'smart_mode_acc_threshold':frame_data.smart_mode_threshold+'mg',
					'max_tx_raw_samples': frame.slice(34, 36).reduce(msbLsb),
					'hardware_id': frame.slice(36, 39),
					'reserved': frame.slice(39, 43),
					'tx_lifetime_counter': frame.slice(43, 46).reduce(msbLsb),
					'machine_values': {
						'firmware': frame[2],
						'destination_address': toMac(frame.slice(12, 16), false),
						'mode': frame[16],
						'odr': frame[17],
						'sampling_duration': frame[18],
						'filter_status': frame[19],
						'lpf_coeff': frame[20],
						'hpf_coeff': frame[21],
						'sampling_interval': frame[22],
						'on_request_timeout': frame[23],
						'deadband': frame[24],
						'payload_length': frame[25],
						'fsr': frame[26],
						'auto_raw_destination_address': toMac(frame.slice(27 , 31), false),
						'auto_raw_interval': frame[31],
						'smart_mode_skip_count': frame[32],
						'smart_mode_acc_threshold':frame[33],
						'max_tx_raw_samples': frame.slice(34, 36),
						'hardware_id': frame.slice(36, 39),
						'reserved': frame.slice(39, 43),
						'tx_lifetime_counter': frame.slice(43, 46)
					}
				}
			}
		},
		'1010': {
			name: 'RS232 Wireless Converter',
			parse: (d) => {
				return {
					data: d
				};
			},
		},
		'1011': {
			name: 'RS485 Wireless Converter',
			parse: (d) => {
				return {
					data: d
				};
			},
		},
		'10000': {
			name: '4-Relay',
			parse: (d) => {
				return {
					relay_1: d[0] & 1 ? 1 : 0,
					relay_2: d[0] & 2 ? 1 : 0,
					relay_3: d[0] & 4 ? 1 : 0,
					relay_4: d[0] & 8 ? 1 : 0
				};
			},
			control: (msg) => {
				switch(msg.topic){
					case 'all':
						return [3, parseInt(msg.payload)];
					case 'get_status':
						return [2];
					default:
						return [parseInt(msg.payload), parseInt(msg.topic.split('_').pop())];
				}
			}
		},
		'10006':{
			name: '4-Channel 4-20 mA Input',
			parse: (d) => {
				var readings = {};
				for(var i=0;i++;i<4) readings[`channel_${i+1}`] = d.slice((i*2), 1+(i*2)).reduce(msbLsb) / 100;
				return readings;
			}
		},
		'10007':{
			name: '4-Channel Current Monitor',
			parse: (d) => {
				var readings = {};
				for(var i=0;i++;i<4) readings[`channel_${i+1}`] = d.slice((i*3), 2+(i*3)).reduce(msbLsb) / 1000;
				return readings;
			}
		},
		'10012':{
			name: '2-Relay + 2-Input',
			parse: (d) => {
				return {
					relay_1: d[0] & 1 ? 1 : 0,
					relay_2: d[0] & 2 ? 1 : 0,
					input_1: d[1] & 1 ? 1 : 0,
					input_2: d[1] & 2 ? 1 : 0
				};
			},
			control: (msg) => {

				switch(msg.topic){
					case 'all':
						return [3, parseInt(msg.payload)];
					case 'get_status':
						return [2];
					default:
						return [parseInt(msg.payload), parseInt(msg.topic.split('_').pop())];
				}
			}
		},
	};
	return types;
}
function chunkString1(str, len) {
	var _length = str.length,
		_size = Math.ceil(_length/len),
		_ret  = [];
	for(var _i=0; _i<_length; _i+=len) {
		_ret.push(str.substring(_i, _i + len));
	}
	return _ret;
}
function mac2bytes(mac){
	return mac.split(':').map((v) => parseInt(v, 16));
}
function msbLsb(m,l){return (m<<8)+l;}
function toHex(n){return ('00' + n.toString(16)).substr(-2);}

function toMac(arr, add_colon = true){
	if(add_colon){
		return arr.reduce((h,c,i) => {return (i==1?toHex(h):h)+':'+toHex(c);});
	}else{
		return arr.reduce((h,c,i) => {return (i==1?toHex(h):h)+toHex(c);});
	}
}
function byte2mac(h,c,i){return h.constructor == Array ? h.reduce(byte2mac) : (i==1?h.toHex():h)+':'+c.toHex();}
function int2Bytes(i, l){
	var bits = i.toString(2);
	if(bits.length % 8) bits = ('00000000' + bits).substr(bits.length % 8);
	var bytes = chunkString1(bits, 8).map((v) => parseInt(v, 2));
	if(bytes.length < l){
		while(bytes.length < l){
			bytes.unshift(0);
		}
	}
	return bytes;
}
function msbLsb_to_SignedInt(value, addition) {
	const bigValue = BigInt(value);
	const bigAddition = BigInt(addition);

	let result = (bigValue << 8n) + bigAddition;

	// Always return the result as an unsigned 64-bit BigInt.
	return BigInt.asIntN(64, result);
};
function msbLsb_to_UnsignedInt(value, addition) {
	const bigValue = BigInt(value);
	const bigAddition = BigInt(addition);

	let result = (bigValue << 8n) + bigAddition;

	// Always return the result as a signed 64-bit BigInt.
	return BigInt.asUintN(64, result);
};
// TODO implement this function in future for decimal formatting on BigInt values
// Untested. Requires multiplication by scale factor to account for decimal value loss before passing in. Might handle that in this function as well.
// function formatFixedPoint(bigIntValue, decimalPlaces) {
//     let sign = bigIntValue < 0n ? "-" : "";
//     let absValue = bigIntValue < 0n ? -bigIntValue : bigIntValue;

//     let s = absValue.toString();
//     if (s.length <= decimalPlaces) {
//         s = '0'.repeat(decimalPlaces - s.length + 1) + s;
//     }

//     let decimalIndex = s.length - decimalPlaces;
//     return sign + s.substring(0, decimalIndex) + '.' + s.substring(decimalIndex);
// }
function signInt(i, b){
	if(i.toString(2).length != b) return i;
	return -(((~i) & ((1 << (b-1))-1))+1);
}

//signInt=(d,b) => d>1<<(b-2)?0-((1<<b)-d):d;
function bytesToString(byteArray) {
  let result = "";
  
  for (let i = 0; i < byteArray.length; i++) {
    // 0x00 (or 0) represents empty/null bytes. We want to skip them.
    if (byteArray[i] !== 0) {
      result += String.fromCharCode(byteArray[i]);
    }
  }
  
  return result;
}