/****************************************************************/ /* Stanton SCS.1m MIDI controller script v1.20 */ /* Copyright (C) 2009-2011, Sean M. Pappalardo */ /* but feel free to tweak this to your heart's content! */ /* For Mixxx version 1.9.x */ /****************************************************************/ function StantonSCS1m() {} // ---------- Customization variables ---------- // See http://mixxx.org/wiki/doku.php/stanton_SCS.1m_mixxx_user_guide for details StantonSCS1m.faderStart = false; // Allows decks to start when their channel or cross fader is opened (toggleable with the top button) StantonSCS1m.scratchFactor = 2; // Adjusts the speed of scratching with the select knob // ---------- Other global variables ---------- StantonSCS1m.debug = false; // Enable/disable debugging messages to the console StantonSCS1m.id = ""; // The ID for the particular device being controlled for use in debugging, set at init time StantonSCS1m.channel = 0; // MIDI channel the device is on StantonSCS1m.swVersion = "1.9"; // Mixxx version for display StantonSCS1m.sysex = [0xF0, 0x00, 0x01, 0x02]; // Preamble for all SysEx messages for this device StantonSCS1m.modifier = { }; // Modifier buttons (allowing alternate controls) defined on-the-fly if needed StantonSCS1m.selectKnobMode = "browse"; // Current mode for the gray select knob StantonSCS1m.inSetup = false; // Flag for if the device is in setup mode StantonSCS1m.revtime = 1.8; // Time in seconds for the virtual record to spin once. Used for calculating the position LEDs (1.8 for 33 1/3 RPM) StantonSCS1m.trackDuration = [0,0]; // Duration of the song on each deck (used for vinyl LEDs) StantonSCS1m.state = { }; // Temporary state variables StantonSCS1m.timer = { }; // Temporary storage of timer IDs StantonSCS1m.lastLight = [-1,-1]; // Last circle LED values StantonSCS1m.lastTime = ["-99:99","-99:99"]; // Last time remaining values StantonSCS1m.lastVu = { 92:0, 93:0, 94:0, 95:0, 96:0, 97:0 }; // Last VU meter values StantonSCS1m.lastCrossFader = 0; // Last value of the cross fader StantonSCS1m.lastFader = [0,0]; // Last value of each channel fader StantonSCS1m.scratchDeck = 0; // Current deck being scratched with the select knob StantonSCS1m.scratch = { "rpm":(33+1/3)*StantonSCS1m.scratchFactor, "resolution":30, "alpha":1.0/8, "beta":(1.0/8)/32 }; StantonSCS1m.hotCueDeck = 1; // Current hot cue page StantonSCS1m.hotCues = { 1:{ 35:1, 36:2, 37:3, 38:4 }, 2:{ 35:1, 36:2, 37:3, 38:4 } }; // Signals to (dis)connect: Group, Key, Function name StantonSCS1m.hotcueSignals = [ ["CurrentChannel", "hotcue_1_enabled", "StantonSCS1m.Preset1LED"], ["CurrentChannel", "hotcue_2_enabled", "StantonSCS1m.Preset2LED"], ["CurrentChannel", "hotcue_3_enabled", "StantonSCS1m.Preset3LED"], ["CurrentChannel", "hotcue_4_enabled", "StantonSCS1m.Preset4LED"] ]; // ---------- Functions ---------- StantonSCS1m.init = function (id) { // called when the MIDI device is opened & set up StantonSCS1m.id = id; // Store the ID of this device for later use midi.sendSysexMsg(StantonSCS1m.sysex.concat([StantonSCS1m.channel, 17, 0xF7]),7); // Extinguish all LEDs // var CC = 0xB0 + StantonSCS1m.channel; var No = 0x90 + StantonSCS1m.channel; // Welcome message var message = "Welcome"; midi.sendSysexMsg(StantonSCS1m.sysex.concat([StantonSCS1m.channel, 1],message.toInt(), 0xF7),7+message.length); // Set LCD1 midi.sendShortMsg(No,49,127); // to orange message = "to"; midi.sendSysexMsg(StantonSCS1m.sysex.concat([StantonSCS1m.channel, 2],message.toInt(), 0xF7),7+message.length); // Set LCD2 midi.sendShortMsg(No,49+1,127); // to orange message = "Mixxx v"; midi.sendSysexMsg(StantonSCS1m.sysex.concat([StantonSCS1m.channel, 3],message.toInt(), 0xF7),7+message.length); // Set LCD3 midi.sendShortMsg(No,49+2,127); // to orange midi.sendSysexMsg(StantonSCS1m.sysex.concat([StantonSCS1m.channel, 4],StantonSCS1m.swVersion.toInt(), 0xF7),7+StantonSCS1m.swVersion.length); // Set LCD4 midi.sendShortMsg(No,49+3,32); // to red // midi.sendSysexMsg(StantonSCS1m.sysex.concat([StantonSCS1m.channel, 16, 0xF7]),7); // Light all LEDs // for (i=0x48; i<=0x5c; i++) midi.sendShortMsg(No,i,0x40); // Set surface LEDs to black default // Connect signals // Vu Meters engine.connectControl("[Master]","VuMeterL","StantonSCS1m.MasterLVu"); engine.connectControl("[Master]","VuMeterR","StantonSCS1m.MasterRVu"); engine.connectControl("[Channel1]","VuMeter","StantonSCS1m.Channel1Vu"); engine.connectControl("[Channel2]","VuMeter","StantonSCS1m.Channel2Vu"); // Clipping LED engine.connectControl("[Master]","PeakIndicator","StantonSCS1m.MasterClip"); engine.connectControl("[Channel1]","PeakIndicator","StantonSCS1m.Channel1Clip"); engine.connectControl("[Channel2]","PeakIndicator","StantonSCS1m.Channel2Clip"); // Pitch displays engine.connectControl("[Channel1]","rate","StantonSCS1m.pitchDisplay1"); engine.connectControl("[Channel2]","rate","StantonSCS1m.pitchDisplay2"); // Pitch display colors engine.connectControl("[Channel1]","rateRange","StantonSCS1m.pitchColor1"); engine.connectControl("[Channel2]","rateRange","StantonSCS1m.pitchColor2"); // Virtual platter LEDs & time displays engine.connectControl("[Channel1]","visual_playposition","StantonSCS1m.positionUpdates1"); engine.connectControl("[Channel2]","visual_playposition","StantonSCS1m.positionUpdates2"); engine.connectControl("[Channel1]","duration","StantonSCS1m.durationChange1"); engine.connectControl("[Channel2]","duration","StantonSCS1m.durationChange2"); // Faders engine.connectControl("[Master]","crossfader","StantonSCS1m.crossFaderStart"); engine.connectControl("[Channel1]","volume","StantonSCS1m.ch1FaderStart"); engine.connectControl("[Channel2]","volume","StantonSCS1m.ch2FaderStart"); // Initialize the vinyl LEDs if the mapping is loaded after a song is StantonSCS1m.durationChange1(engine.getValue("[Channel1]","duration")); StantonSCS1m.durationChange2(engine.getValue("[Channel2]","duration")); // Force change to first deck, initializing the LEDs and connecting signals in the process // Set active deck to last available so the below will switch to #1. StantonSCS1m.hotCueDeck = engine.getValue("[Master]","num_decks"); StantonSCS1m.hotCueDeckChange(StantonSCS1m.channel, 34, 0x7F, 0x90+StantonSCS1m.channel); StantonSCS1m.hotCueDeckChange(StantonSCS1m.channel, 34, 0x00, 0x80+StantonSCS1m.channel); // Set LCD text StantonSCS1m.initLCDs(); StantonSCS1m.browseButton(StantonSCS1m.channel, 0x20, 0x7F, 0x90+StantonSCS1m.channel); // Force into browse mode midi.sendSysexMsg(StantonSCS1m.sysex.concat([StantonSCS1m.channel, 34, 32, 0xF7]),8); // Get position of all pots // Button LEDs if (StantonSCS1m.faderStart) midi.sendShortMsg(No,8,1); var LEDInit = [ ["Channels","rate_temp_up"], ["Channels","rate_temp_down"], ["Channels","pfl"], ["Channels","beatsync"], ["Channels","play"], ["Channels","cue_default"] ]; for (i=0; i-1.0) { // ...light the button red to show acknowledgement of the press but don't load midi.sendShortMsg(0x90+channel,control,64); print ("StantonSCS1m: Not loading into deck 2 because it's playing to the Master output."); } else { midi.sendShortMsg(0x90+channel,control,127); // Orange engine.setValue("[Channel2]","LoadSelectedTrack",1); } break; } return; } // Button up switch (StantonSCS1m.selectKnobMode) { case "control": if (StantonSCS1m.scratchDeck==2) { engine.scratchDisable(StantonSCS1m.scratchDeck); StantonSCS1m.scratchDeck=0; } midi.sendShortMsg(0x90+channel,control,32); // green break; case "browse": midi.sendShortMsg(0x80+channel,control,0); // Off engine.setValue("[Channel2]","LoadSelectedTrack",0); break; } StantonSCS1m.modifier["enterButton"]=0; } StantonSCS1m.encoderJog1 = function (channel, control, value, status) { StantonSCS1m.encoderJog(value,1); } StantonSCS1m.encoderJog2 = function (channel, control, value, status) { StantonSCS1m.encoderJog(value,2); } StantonSCS1m.encoderJog = function (value,deck) { if (StantonSCS1m.checkInSetup()) return; jogValue=(value-64); if (engine.getValue("[Channel"+deck+"]","play")==1 && engine.getValue("[Channel"+deck+"]","reverse")==1) jogValue= -(jogValue); engine.setValue("[Channel"+deck+"]","jog",jogValue*4); } StantonSCS1m.pitchRangeKnob1 = function (channel, control, value, status) { StantonSCS1m.pitchRangeKnob(value,1); } StantonSCS1m.pitchRangeKnob2 = function (channel, control, value, status) { StantonSCS1m.pitchRangeKnob(value,2); } StantonSCS1m.pitchRangeKnob = function (value,deck) { if (StantonSCS1m.checkInSetup()) return; var currentValue = engine.getValue("[Channel"+deck+"]","rateRange"); var newValue = Math.round(currentValue*100+(value-64))*0.01; if (newValue<=0.01) newValue=0.01; if (newValue>1) newValue=1; engine.setValue("[Channel"+deck+"]","rateRange",newValue); engine.trigger("[Channel"+deck+"]","rate"); // Force GUI to update StantonSCS1m.pitchDisplay(engine.getValue("[Channel"+deck+"]","rate"),deck); // Force LCDs to update } StantonSCS1m.faderStartToggle = function (channel, control, value, status) { StantonSCS1m.faderStart = !StantonSCS1m.faderStart; var No = 0x90 + channel; if (StantonSCS1m.faderStart) midi.sendShortMsg(No,8,1); else midi.sendShortMsg(No,8,0); } StantonSCS1m.hotCueDeckChange = function (channel, control, value, status) { if (StantonSCS1m.checkInSetup()) return; if ((status & 0XF0) == 0x90) { // If button down StantonSCS1m.modifier["hotCueToggle"]=1; // Set modifier flag (to allow cue deletion) StantonSCS1m.modifier["hotCueToggleTime"] = new Date(); // Store the current time in milliseconds } else { // If button up StantonSCS1m.modifier["hotCueToggle"]=0; // If the button was held down for over 1/3 of a second, stay on the current cue page if (StantonSCS1m.modifier["hotCueToggleTime"] != 0.0 && ((new Date() - StantonSCS1m.modifier["hotCueToggleTime"])>300)) return; StantonSCS1m.connectPresetSignals(channel,true); // Disconnect previous ones if (StantonSCS1m.hotCueDeck == engine.getValue("[Master]","num_decks")) StantonSCS1m.hotCueDeck=1; else StantonSCS1m.hotCueDeck++; // Change bank button color if (StantonSCS1m.hotCueDeck % 2 == 0) midi.sendShortMsg(0x90 + channel,34,64); // On else midi.sendShortMsg(0x80 + channel,34,0); // Off StantonSCS1m.connectPresetSignals(channel,false); // Connect new ones } } StantonSCS1m.presetButton = function (channel, control, value, status) { if (StantonSCS1m.checkInSetup()) return; var deck = StantonSCS1m.hotCueDeck; if ((status & 0xF0) == 0x90) { // If button down // Multiple cue points if (StantonSCS1m.modifier["hotCueToggle"]==1) { // Delete cue point engine.setValue("[Channel"+deck+"]","hotcue_"+StantonSCS1m.hotCues[deck][control]+"_clear",1); engine.setValue("[Channel"+deck+"]","hotcue_"+StantonSCS1m.hotCues[deck][control]+"_clear",0); } else { // If hotcue X is set, seeks the player to hotcue X's position. // If hotcue X is not set, sets hotcue X to the current play position. engine.setValue("[Channel"+deck+"]","hotcue_"+StantonSCS1m.hotCues[deck][control]+"_activate",1); } return; } // Button up engine.setValue("[Channel"+deck+"]","hotcue_"+StantonSCS1m.hotCues[deck][control]+"_activate",0); } // ---------- Slot functions ---------- StantonSCS1m.crossFaderStart = function (value) { if (!StantonSCS1m.faderStart) return; if (StantonSCS1m.lastCrossFader==value) return; if (value==-1.0 && engine.getValue("[Channel2]","play")==1) { engine.setValue("[Channel2]","cue_default",1); engine.setValue("[Channel2]","cue_default",0); } if (value==1.0 && engine.getValue("[Channel1]","play")==1) { engine.setValue("[Channel1]","cue_default",1); engine.setValue("[Channel1]","cue_default",0); } if (StantonSCS1m.lastCrossFader==-1.0) engine.setValue("[Channel2]","play",1); if (StantonSCS1m.lastCrossFader==1.0) engine.setValue("[Channel1]","play",1); StantonSCS1m.lastCrossFader=value; } StantonSCS1m.ch1FaderStart = function (value) { StantonSCS1m.channelFaderStart(value,1); } StantonSCS1m.ch2FaderStart = function (value) { StantonSCS1m.channelFaderStart(value,2); } StantonSCS1m.channelFaderStart = function (value,deck) { if (!StantonSCS1m.faderStart) return; if (StantonSCS1m.lastFader[deck]==value) return; if (value==0 && engine.getValue("[Channel"+deck+"]","play")==1) { engine.setValue("[Channel"+deck+"]","cue_default",1); engine.setValue("[Channel"+deck+"]","cue_default",0); } if (StantonSCS1m.lastFader[deck]==0) engine.setValue("[Channel"+deck+"]","play",1); StantonSCS1m.lastFader[deck]=value; } // ---------- LED slot functions ---------- StantonSCS1m.buttonLED = function (value, note, on, off) { var byte1 = 0x90 + StantonSCS1m.channel; if (value>0) midi.sendShortMsg(byte1,note,on); else midi.sendShortMsg(byte1,note,off); } StantonSCS1m.Preset1LED = function(value) { StantonSCS1m.PresetLED(value,35); } StantonSCS1m.Preset2LED = function(value) { StantonSCS1m.PresetLED(value,36); } StantonSCS1m.Preset3LED = function(value) { StantonSCS1m.PresetLED(value,37); } StantonSCS1m.Preset4LED = function(value) { StantonSCS1m.PresetLED(value,38); } StantonSCS1m.PresetLED = function(value,control) { if (value>0) midi.sendShortMsg(0x90 + StantonSCS1m.channel,control,64); else midi.sendShortMsg(0x80 + StantonSCS1m.channel,control,0); } // Vu Meters StantonSCS1m.Channel1Vu = function (value) { StantonSCS1m.VuMeter(value,93); } StantonSCS1m.Channel2Vu = function (value) { StantonSCS1m.VuMeter(value,94); } StantonSCS1m.MasterLVu = function (value) { StantonSCS1m.VuMeter(value,96); } StantonSCS1m.MasterRVu = function (value) { StantonSCS1m.VuMeter(value,97); } StantonSCS1m.Channel1Clip = function (value) { StantonSCS1m.clipLED(value,93); } StantonSCS1m.Channel2Clip = function (value) { StantonSCS1m.clipLED(value,94); } StantonSCS1m.MasterClip = function (value) { StantonSCS1m.clipLED(value,96); StantonSCS1m.clipLED(value,97); } StantonSCS1m.clipLED = function (value, note) { var on = 0x90 + StantonSCS1m.channel; var off = 0x80 + StantonSCS1m.channel; if (value>0) midi.sendShortMsg(on,note,8); else midi.sendShortMsg(off,note,8); } StantonSCS1m.VuMeter = function (value,note) { var range = 1/7; // keep the highest one for the clip signal var newLEDs=0; if (value>=range*7) newLEDs = 255; else if (value>range*6) newLEDs = 127; else if (value>range*5) newLEDs = 63; else if (value>range*4) newLEDs = 31; else if (value>range*3) newLEDs = 15; else if (value>range*2) newLEDs = 7; else if (value>range) newLEDs = 3; else if (value>0.001) newLEDs = 1; // Last one XOR this one gives the LEDs that changed state var change = StantonSCS1m.lastVu[note] ^ newLEDs; StantonSCS1m.lastVu[note] = newLEDs; if (change == 0) return; var on = 0x90 + StantonSCS1m.channel; var off = 0x80 + StantonSCS1m.channel; var compare=1; for (i=0; i<=7; i++) { if((change & compare) !=0) { // If this LED changed if ((newLEDs & compare) !=0) { // If the new value is not 0 midi.sendShortMsg(on,note,i); // Light it up } else midi.sendShortMsg(off,note,i); // Turn it off } compare <<= 1; } } // LCD displays ("scribble strips") StantonSCS1m.pitchDisplay1 = function (value) { if (StantonSCS1m.inSetup) return; StantonSCS1m.pitchDisplay(value,1); } StantonSCS1m.pitchDisplay2 = function (value) { if (StantonSCS1m.inSetup) return; StantonSCS1m.pitchDisplay(value,2); } StantonSCS1m.pitchDisplay = function (value,deck) { var data = (value * engine.getValue("[Channel"+deck+"]","rateRange") * 100); if (engine.getValue("[Channel"+deck+"]","rate_dir") == -1) data=-data; // Show the correct sign depending on the preferences data = data.toFixed(2); // Always two decimal places if (data>0) data = "+"+data; data = data + "%"; var message = ""; for (i=data.length; i<8; i++) message += " "; // Pad the left side with spaces so the decimal point doesn't move around message += data; var LCD; switch (deck) { case 1: LCD=1; break; case 2: LCD=4; break; } midi.sendSysexMsg(StantonSCS1m.sysex.concat([StantonSCS1m.channel, LCD],message.toInt(), 0xF7),7+message.length); // Set LCD with pitch value } StantonSCS1m.pitchColor1 = function (value) { StantonSCS1m.pitchColor(value,1); } StantonSCS1m.pitchColor2 = function (value) { StantonSCS1m.pitchColor(value,2); } StantonSCS1m.pitchColor = function (value, deck) { var No = 0x90 + StantonSCS1m.channel; var offset=0; if (deck==2) offset=3; if (value<=0.25) midi.sendShortMsg(No,49+offset,96); else if (value<=0.5) midi.sendShortMsg(No,49+offset,127); else if (value>0.5) midi.sendShortMsg(No,49+offset,32); engine.trigger("[Channel"+deck+"]","rate"); // Force pitch display to update } // Virtual platter rings & time displays StantonSCS1m.durationChange1 = function (value) { // Stop any leftover end-of-track-flash timers var deck = 1; if (StantonSCS1m.timer["15s-d"+deck] != -1) { engine.stopTimer(StantonSCS1m.timer["15s-d"+deck]); StantonSCS1m.timer["15s-d"+deck] = -1; } if (StantonSCS1m.timer["30s-d"+deck] != -1) { engine.stopTimer(StantonSCS1m.timer["30s-d"+deck]); StantonSCS1m.timer["30s-d"+deck] = -1; } // Make sure back light is red if (StantonSCS1m.state["flash"+deck]!=false) { StantonSCS1m.state["flash"+deck]=false; midi.sendShortMsg(0x90 + StantonSCS1m.channel,49+deck,32); //red } StantonSCS1m.trackDuration[1]=value; StantonSCS1m.displayFlash[1]=-1; } StantonSCS1m.durationChange2 = function (value) { // Stop any leftover end-of-track-flash timers var deck = 1; if (StantonSCS1m.timer["15s-d"+deck] != -1) { engine.stopTimer(StantonSCS1m.timer["15s-d"+deck]); StantonSCS1m.timer["15s-d"+deck] = -1; } if (StantonSCS1m.timer["30s-d"+deck] != -1) { engine.stopTimer(StantonSCS1m.timer["30s-d"+deck]); StantonSCS1m.timer["30s-d"+deck] = -1; } // Make sure back light is red if (StantonSCS1m.state["flash"+deck]!=false) { StantonSCS1m.state["flash"+deck]=false; midi.sendShortMsg(0x90 + StantonSCS1m.channel,49+deck,32); //red } StantonSCS1m.trackDuration[2]=value; StantonSCS1m.displayFlash[2]=-1; } StantonSCS1m.positionUpdates1 = function (value) { StantonSCS1m.positionUpdates(value,1); } StantonSCS1m.positionUpdates2 = function (value) { StantonSCS1m.positionUpdates(value,2); } StantonSCS1m.displayFlash = function (deck) { if (StantonSCS1m.checkInSetup()) return; var No = 0x90 + StantonSCS1m.channel; if (!StantonSCS1m.state["flash"+deck]) { StantonSCS1m.state["flash"+deck]=true; midi.sendShortMsg(No,49+deck,96); //green } else { StantonSCS1m.state["flash"+deck]=false; midi.sendShortMsg(No,49+deck,32); //red } } StantonSCS1m.positionUpdates = function (value,deck) { var No = 0x90 + StantonSCS1m.channel; var trackTimeRemaining = ((1-value) * StantonSCS1m.trackDuration[deck]) | 0; // OR with 0 replaces Math.floor and is faster // Flash near the end of the track if the track is longer than 30s if (StantonSCS1m.trackDuration[deck]>30) { if (trackTimeRemaining<=30 && trackTimeRemaining>15) { // If <30s left, flash slowly if (StantonSCS1m.timer["30s-d"+deck] == -1) { // Start timer StantonSCS1m.timer["30s-d"+deck] = engine.beginTimer(500,"StantonSCS1m.displayFlash("+deck+")"); if (StantonSCS1m.timer["15s-d"+deck] != -1) { // Stop the 15s timer if it was running engine.stopTimer(StantonSCS1m.timer["15s-d"+deck]); StantonSCS1m.timer["15s-d"+deck] = -1; } } } else if (trackTimeRemaining<=15 && trackTimeRemaining>0) { // If <15s left, flash quickly if (StantonSCS1m.timer["15s-d"+deck] == -1) { // Start timer StantonSCS1m.timer["15s-d"+deck] = engine.beginTimer(125,"StantonSCS1m.displayFlash("+deck+")"); if (StantonSCS1m.timer["30s-d"+deck] != -1) { // Stop the 30s timer if it was running engine.stopTimer(StantonSCS1m.timer["30s-d"+deck]); StantonSCS1m.timer["30s-d"+deck] = -1; } } } else { // Stop flashing if (StantonSCS1m.timer["15s-d"+deck] != -1) { engine.stopTimer(StantonSCS1m.timer["15s-d"+deck]); StantonSCS1m.timer["15s-d"+deck] = -1; } if (StantonSCS1m.timer["30s-d"+deck] != -1) { engine.stopTimer(StantonSCS1m.timer["30s-d"+deck]); StantonSCS1m.timer["30s-d"+deck] = -1; } // Make sure back light is red if (StantonSCS1m.state["flash"+deck]!=false) { StantonSCS1m.state["flash"+deck]=false; midi.sendShortMsg(No,49+deck,32); //red } } } // Revolution time of the imaginary record in seconds var revtime = StantonSCS1m.revtime; var currentTrackPos = value * StantonSCS1m.trackDuration[deck]; var revolutions = currentTrackPos/revtime; var light = ((revolutions-((revolutions)|0))*18) | 0; // OR with 0 replaces Math.floor and is faster // print("----------Light="+light+", Revolutions="+revolutions+", Remainder="+(revolutions-Math.floor(revolutions))); if (StantonSCS1m.lastLight[deck]!=light) { // Don't send light commands if there's no visible change var byte1 = 0xB0 + StantonSCS1m.channel; if (deck==1) midi.sendShortMsg(byte1,0x7E,light+1); else midi.sendShortMsg(byte1,0x7D,light+1); StantonSCS1m.lastLight[deck]=light; } // End platter lights if (!StantonSCS1m.inSetup) { // If not in setup mode // Show track time remaining var message = "-"+secondstominutes(trackTimeRemaining); if (StantonSCS1m.lastTime[deck]!=message) { // Only send the message if its different if (trackTimeRemaining>30) midi.sendShortMsg(No,49+deck,32); // Set backlight to red midi.sendSysexMsg(StantonSCS1m.sysex.concat([StantonSCS1m.channel, deck+1],message.toInt(), 0xF7),7+message.length); StantonSCS1m.lastTime[deck]=message; } } }