Last active
August 29, 2015 13:57
-
-
Save kgleeson/9387458 to your computer and use it in GitHub Desktop.
Clock
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
modes <- array(); | |
function getWeather() | |
{ | |
local url = "https://api.forecast.io/forecast/XXXXXXXXXXXX/53.3331,-6.2489?units=si"; | |
local res = http.get(url, {}).sendsync(); | |
if (res.statuscode != 200) | |
{ | |
server.log("Forecast.io Error: " + res.statuscode + " => " + res.body); | |
} else | |
{ | |
try | |
{ | |
return http.jsondecode(res.body); | |
} | |
catch (e) | |
{ | |
server.error("Forecast.io Error: " + e) | |
} | |
} | |
} | |
function getSunTimes(modeNum) | |
{ | |
local response = getWeather(); | |
if(response) { | |
server.log(response.daily.data[0].sunriseTime); | |
local today = response.daily.data[0]; | |
local dRise = date(today.sunriseTime); | |
local dSet = date(today.sunsetTime); | |
local Sun = { rise=((dRise.hour/2)*5 + (dRise.min/12)), down=((dSet.hour/2)*5 + (dSet.min/12)), i=modeNum}; | |
device.send("sun", Sun) | |
server.log(format("Sunrise: %d:%d Sunset: %d:%d", dRise.hour, dRise.min, dSet.hour, dSet.min)); | |
} | |
} | |
function getCurrentTemp(modeNum) | |
{ | |
local response = getWeather(); | |
if(response) | |
{ | |
local temperature = response.currently.temperature; | |
local date = { temp=temperature, i=modeNum }; | |
device.send("temp", date); | |
server.log(format("The temp is %2f°c", temperature)); | |
} | |
} | |
function setModes(names) | |
{ | |
modes = names; | |
generateUrls(); | |
} | |
device.on("setModes", setModes); | |
function generateUrls() | |
{ | |
for(local i = 0; i < modes.len(); i++) | |
{ | |
server.log("Turn " + modes[i] + " On: " + http.agenturl() + "?mode=" + i); | |
} | |
} | |
function checkMode(mode) | |
{ | |
if(modes[mode] == "Sun times") | |
{ | |
server.log("Sun times"); | |
getSunTimes(mode); | |
} | |
if(modes[mode] == "Temperature") | |
{ | |
server.log("Temperature"); | |
getCurrentTemp(mode); | |
} | |
} | |
function requestHandler(request, response) | |
{ | |
// local c_mode; | |
local message = ""; | |
try { | |
// check if the user sent led as a query parameter | |
if ("mode" in request.query) | |
{ | |
local mode = request.query.mode.tointeger(); | |
if((mode < 0) || (mode >= modes.len())) mode = 0; | |
message = modes[mode]; | |
server.log(" Requested " + modes[mode]); | |
checkMode(mode); | |
device.send("mode", mode); | |
} | |
if ("fillclock" in request.query) | |
{ | |
local fillclock = request.query.fillclock.tointeger(); | |
local mode = false; | |
if(fillclock) mode = true; | |
device.send("fillclock", mode); | |
message = format("Fill Clock is %s", mode ? "true" : "false"); | |
} | |
if ("segments" in request.query) | |
{ | |
local segments = request.query.segments.tointeger(); | |
local data = {segments=segments, i=modes.find("Windmill")} | |
device.send("windmill", data); | |
message = format("Windmill segments set to %d", segments); | |
} | |
if ("debug" in request.query) | |
{ | |
local debug = request.query.debug.tointeger(); | |
local mode = false; | |
if(debug == 1) mode = true; | |
server.log("Debug is " + mode); | |
device.send("debug", mode); | |
message = format("Debug is %s", mode ? "true" : "false"); | |
server.log(message); | |
} | |
if ("demo" in request.query) | |
{ | |
local demo = request.query.demo.tointeger(); | |
local mode = false; | |
if(demo == 1) mode = true; | |
server.log("Demo is " + mode); | |
device.send("demo", mode); | |
message = format("Demo is %s", mode ? "true" : "false"); | |
server.log(message); | |
} | |
if ("delay" in request.query) | |
{ | |
local delay = request.query.delay.tofloat(); | |
device.send("delay", delay); | |
message = format("Delay set to %f seconds", delay); | |
server.log(message); | |
} | |
local ip = request.headers["x-forwarded-for"]; | |
server.log("IP Address " + ip); | |
response.send(200, format("OK\n%s", message)); | |
} | |
catch (ex) | |
{ | |
response.send(500, "Internal Server Error: " + ex); | |
} | |
} | |
// register the HTTP handler | |
http.onrequest(requestHandler); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const SPICLK = 7500; // kHz | |
const BYTESPERPIXEL = 24; | |
class NeoPixels | |
{ | |
ZERO = 0xC0; | |
ONE = 0xF8; | |
bits = null; | |
clearblob = blob(BYTESPERPIXEL); | |
spi = null; // imp SPI interface (pre-configured) | |
frameSize = null; // number of pixels per frame | |
frame = null; // a blob to hold the current frame | |
constructor(_spi, _frameSize) | |
{ | |
this.spi = _spi; | |
this.frameSize = _frameSize; | |
this.frame = blob(frameSize*BYTESPERPIXEL + 1); | |
// prepare the bits array and the clearblob blob | |
initialize(); | |
clearFrame(); | |
writeFrame(); | |
} | |
function initialize() | |
{ | |
bits = array(256); | |
for (local i = 0; i < 256; i++) | |
{ | |
local valblob = blob(BYTESPERPIXEL / 3); | |
valblob.writen((i & 0x80) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x40) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x20) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x10) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x08) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x04) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x02) ? ONE:ZERO,'b'); | |
valblob.writen((i & 0x01) ? ONE:ZERO,'b'); | |
bits[i] = valblob; | |
} | |
// now fill the clearblob | |
for(local j = 0; j < BYTESPERPIXEL; j++) | |
{ | |
clearblob.writen(ZERO, 'b'); | |
} | |
// must have a null at the end to drive MOSI low | |
clearblob.writen(0x00,'b'); | |
} | |
function writePixel(p, color) | |
{ | |
frame.seek(p*BYTESPERPIXEL); | |
// red and green are swapped for some reason, so swizzle them back | |
frame.writeblob(bits[color[1]]); | |
frame.writeblob(bits[color[0]]); | |
frame.writeblob(bits[color[2]]); | |
} | |
function clearFrame() | |
{ | |
frame.seek(0); | |
for (local p = 0; p < frameSize; p++) frame.writeblob(clearblob); | |
} | |
function writeFrame() | |
{ | |
spi.write(frame); | |
} | |
} | |
RED <- [255,0,0]; | |
GREEN <- [0,255,0]; | |
BLUE <- [0,0,255]; | |
CYAN <- [0,255,255]; | |
WHITE <- [255,255,255]; | |
WARMWHITE <- [253,245,230]; | |
MAGENTA <- [255,0,255]; | |
YELLOW <- [255,255,0]; | |
PACYELLOW <- [100,100,0]; | |
ORANGE <- [255,165,0]; | |
PINK <- [255,192,203]; | |
PURPLE <- [128,0,128]; | |
OLDLACE <- [255,192,203]; | |
BLACK <- [0,0,0]; | |
DARKGREY <- [1,1,1]; | |
LIGHTGREY <- [20,20,20]; | |
const NUMPIXELS = 60; | |
spi <- hardware.spi257; | |
spi.configure(MSB_FIRST, SPICLK); | |
pixelStrip <- NeoPixels(spi, NUMPIXELS); | |
function logToServer(message) | |
{ | |
if(debug) server.log(message); | |
} | |
function colourString(colour) | |
{ | |
return format("[%d, %d, %d]", colour[0], colour[1], colour[2]); | |
} | |
class Mode | |
{ | |
startTime = null; | |
name = null; | |
pixels = null; | |
colourWheel = null; | |
constructor() | |
{ | |
logToServer("Creating pixels with len of " + NUMPIXELS); | |
pixels = array(NUMPIXELS); | |
clearPixels(); | |
} | |
function timer(stop=false) | |
{ | |
if(debug) | |
{ | |
if(!stop) | |
{ | |
startTime = hardware.millis(); | |
} else | |
{ | |
logToServer(format("Took %fms", hardware.millis()-startTime)); | |
} | |
} | |
} | |
function outputArray() | |
{ | |
if(debug) | |
{ | |
local message = ""; | |
for(local i = 0; i < NUMPIXELS; i++) | |
{ | |
message = format("%s%s", message, colourString(pixels[i])); | |
} | |
server.log(message); | |
} | |
} | |
function drawPixels() | |
{ | |
for(local i = 0; i < NUMPIXELS; i++) | |
{ | |
logToServer("Writing pixel " + i + " to " + colourString(pixels[i])); | |
pixelStrip.writePixel(i, pixels[i]); | |
} | |
} | |
function setPixel(i, colour) | |
{ | |
pixels[i] = colour; | |
logToServer("Set pixels[" + i + "] to " + colourString(pixels[i])); | |
} | |
function max(num, maxnum=0) | |
{ | |
return (num > maxnum) ? num : maxnum; | |
} | |
function setColour(colour, fadeby=1) | |
{ | |
local fadedColour = [(colour[0]*fadeby).tointeger(), (colour[1]*fadeby).tointeger(), (colour[2]*fadeby).tointeger()]; | |
logToServer(colourString(fadedColour)); | |
return fadedColour; | |
} | |
function rotate(num=1) | |
{ | |
for(local i = 0; i < num; i++) | |
{ | |
local lastPixel = pixels.pop(); | |
pixels.insert(0, lastPixel); | |
} | |
} | |
function clearPixels(colour=BLACK) | |
{ | |
for(local i = 0; i < NUMPIXELS; i++) | |
{ | |
setPixel(i, colour); | |
} | |
} | |
function genColourWheel() | |
{ | |
colourWheel = array(); | |
local steps = 20; | |
local rgbColour = [255, 0, 0]; | |
for(local decColour = 0; decColour < 3; decColour++) | |
{ | |
local incColour = (decColour == 2) ? 0 : decColour + 1; | |
for(local i = 0; i < steps; i++) | |
{ | |
local changeBy = 255/steps; | |
rgbColour[decColour] = changeColourBy(rgbColour[decColour], -(changeBy)); | |
rgbColour[incColour] = changeColourBy(rgbColour[incColour], changeBy); | |
logToServer("Inserting " + colourString(rgbColour)); | |
colourWheel.insert(0, [rgbColour[0], rgbColour[1], rgbColour[2]]); | |
} | |
} | |
if(debug) | |
{ | |
logToServer("Generated Array:") | |
for(local i = 0; i < colourWheel.len() ; i++) | |
{ | |
logToServer("[" + i + "] " + colourString(colourWheel[i])); | |
} | |
} | |
} | |
function getFromColourWheel() | |
{ | |
genColourWheel(); | |
for(local i = 0 ;; i++) | |
{ | |
i = i % colourWheel.len(); | |
logToServer("Yielding [" + i + "] " + colourString(colourWheel[i])); | |
yield colourWheel[i]; | |
} | |
} | |
function changeColourBy(colour, value) | |
{ | |
local returnValue = colour + value; | |
returnValue = (returnValue < 0) ? 0 :returnValue; | |
returnValue = (returnValue > 255) ? 255 : returnValue; | |
return returnValue.tointeger(); | |
} | |
function draw() | |
{ | |
timer(); | |
pixelStrip.clearFrame(); | |
update(); | |
drawPixels(); | |
pixelStrip.writeFrame(); | |
imp.wakeup(delay, setMode); | |
timer(true); | |
} | |
} | |
class Clock extends Mode | |
{ | |
lastSec = null; | |
fillClock = null; | |
PENTDIVCOL = null; | |
DIVCOL = null; | |
SECCOL = null; | |
MINCOL = null; | |
HOURCOL = null; | |
constructor(_fillClock=false) | |
{ | |
base.constructor(); | |
lastSec = date(time()).sec; | |
PENTDIVCOL = LIGHTGREY; | |
DIVCOL = DARKGREY; | |
SECCOL = RED; | |
MINCOL = GREEN; | |
HOURCOL = BLUE; | |
fillClock = _fillClock; | |
name = "Clock" | |
} | |
function update() | |
{ | |
local d = date(time()); | |
logToServer("Clocking"); | |
logToServer(format("%d:%d:%02d", d.hour, d.min, d.sec)); | |
local hourPixel = ((d.hour % 12) * 5); | |
for(local i = 0; i < NUMPIXELS; i++) | |
{ | |
setPixel(i, DIVCOL); | |
if(i % 5 == 0) setPixel(i, PENTDIVCOL); | |
if(fillClock) | |
{ | |
local secondGreatThanMinAndHour = ((i < d.sec) && (d.sec > d.min) && (d.sec > (hourPixel))); | |
local minLessThanHour = ((d.min < (hourPixel) && (i < d.min))); | |
if(secondGreatThanMinAndHour) setPixel(i, SECCOL); | |
if(i < d.min) setPixel(i, MINCOL); | |
if(i < hourPixel) setPixel(i, HOURCOL); | |
if(minLessThanHour) setPixel(i, MINCOL); | |
} | |
if(d.min == hourPixel) | |
{ | |
setPixel(d.min-1, HOURCOL); | |
setPixel(d.min+1, HOURCOL); | |
} | |
if(i == hourPixel) setPixel(i, HOURCOL); | |
if(i == d.min) setPixel(i, MINCOL); | |
if(i == d.sec) setPixel(i, SECCOL); | |
} | |
} | |
} | |
class Pac extends Mode | |
{ | |
direction = null; | |
offset = null; | |
start = null; | |
constructor() | |
{ | |
base.constructor(); | |
direction = 1; | |
offset = 0; | |
start = 15; | |
name = "Pac"; | |
} | |
function update() | |
{ | |
for(local i = 0; i < NUMPIXELS; i++) | |
{ | |
setPixel(i, PACYELLOW); | |
} | |
for(local i = start; i < start+offset; i++) | |
{ | |
setPixel(i+offset, BLACK); | |
setPixel(i, BLACK); | |
setPixel(i-offset, BLACK); | |
} | |
offset = offset + direction; | |
if(offset >= 5 || offset == 0 ) direction *= -1; | |
logToServer("Pacing"); | |
} | |
} | |
class Chase extends Mode | |
{ | |
chase_start = 0; | |
constructor() | |
{ | |
base.constructor(); | |
name = "Chase"; | |
clearPixels(); | |
setPixel(chase_start, RED); | |
} | |
function update() | |
{ | |
logToServer("Chasing"); | |
rotate() | |
} | |
} | |
class Rainbow extends Mode | |
{ | |
genColour = null; | |
rgbColour = null; | |
constructor() | |
{ | |
base.constructor(); | |
genColour = getColour(); | |
name = "Rainbow"; | |
clearPixels(); | |
} | |
function getColour() | |
{ | |
local colours = [RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE]; | |
for(local i = 0 ;; i++) | |
{ | |
i = i % colours.len(); | |
local count = NUMPIXELS/colours.len(); | |
for(local j=0 ; j < count ; j++) | |
{ | |
// server.log(colourString(colours[i])); | |
yield colours[i]; | |
} | |
} | |
} | |
function update() | |
{ | |
logToServer("Rainbowing"); | |
local lastPixel = pixels.pop(); | |
local colour = resume genColour; | |
logToServer("Pixel is: " + colourString(colour)); | |
pixels.insert(0, colour); | |
} | |
} | |
class Segment extends Mode | |
{ | |
lit = null; | |
length = null; | |
colour = null; | |
pixels = null; | |
constructor(_lit, _length, _colour) | |
{ | |
lit = _lit; | |
length = _length; | |
colour = _colour; | |
pixels = array(length); | |
for(local i = 0; i < length; i++) | |
{ | |
local setColour = (i <= lit) ? colour : BLACK; | |
setPixel(i, setColour); | |
} | |
} | |
function update() | |
{ | |
rotate(); | |
} | |
} | |
class Windmill extends Mode | |
{ | |
numOfSegments = null; | |
colour = null; | |
length = null; | |
litPerSeg = null; | |
segments = null; | |
constructor(_numOfSegments, _colour) | |
{ | |
base.constructor(); | |
numOfSegments = _numOfSegments; | |
colour = _colour; | |
length = NUMPIXELS / numOfSegments; | |
litPerSeg = length / 3; | |
segments = array(numOfSegments); | |
name = "Windmill" | |
for(local i = 0; i < numOfSegments; i++) | |
{ | |
segments[i] = Segment(litPerSeg, length, colour); | |
} | |
} | |
function update() | |
{ | |
for(local i = 0; i < numOfSegments; i++) | |
{ | |
for(local j = 0; j < length; j++) | |
{ | |
setPixel(( j + i * length), segments[i].pixels[j]); | |
} | |
segments[i].update(); | |
} | |
} | |
} | |
class Kitt extends Mode | |
{ | |
length = null; | |
direction = null; | |
offset = null; | |
fadeArray = null; | |
constructor(_length) | |
{ | |
base.constructor(); | |
length = _length; | |
direction = 1; | |
offset = 0; | |
name = "Kitt"; | |
fadeArray = array(length); | |
for(local i = 0 ; i < fadeArray.len() ; i++) | |
{ | |
fadeArray[i] = [(255-(50*i)).tointeger(),0,0]; | |
} | |
} | |
function update() | |
{ | |
clearPixels(); | |
if(offset+length == NUMPIXELS) | |
{ | |
direction = -1; | |
fadeArray.reverse(); | |
} | |
if(offset == 0) | |
{ | |
direction = 1; | |
fadeArray.reverse(); | |
} | |
for(local i = offset; i < offset+length; i++) | |
{ | |
setPixel(i, fadeArray[i-offset]); | |
} | |
offset = offset+direction; | |
if(offset == NUMPIXELS) offset = 0; | |
} | |
} | |
class Pulse extends Mode | |
{ | |
leds = null; | |
colour1 = null; | |
colour2 = null; | |
constructor(_colour1, _colour2) | |
{ | |
base.constructor(); | |
colour1 = _colour1; | |
colour2 = _colour2; | |
name = "Pulse"; | |
for(local i = 0; i < NUMPIXELS; i++) | |
{ | |
local setColour = (i % 5 != 0) ? colour1 : colour2; | |
setPixel(i, setColour); | |
} | |
} | |
function update() | |
{ | |
local oldPixel = pixels[30]; | |
pixels.remove(30); | |
pixels.insert(0, oldPixel); | |
oldPixel = pixels[30]; | |
pixels.remove(30); | |
pixels.insert(59, oldPixel); | |
} | |
} | |
class Sunlight extends Mode | |
{ | |
rise = null; | |
down = null; | |
NIGHTCOLOUR = null; | |
DAYCOLOUR = null; | |
constructor() | |
{ | |
base.constructor(); | |
rise = 0; | |
down = 0; | |
NIGHTCOLOUR = BLUE; | |
DAYCOLOUR = YELLOW; | |
name = "Sun times"; | |
} | |
function sinFade(i, colour) | |
{ | |
local totalSun = down - rise; | |
local led = i - rise; | |
local sunPi = (totalSun)/PI | |
local fadeby = math.sin(led/sunPi); | |
logToServer("i = " + i + " led = " + led + " sunPi = " + sunPi + " fadeby = " + fadeby); | |
local fadedColour = setColour(colour, fadeby); | |
return fadedColour; | |
} | |
function update() | |
{ | |
local d = date(time()); | |
local now = ((d.hour/2)*5 + (d.min/12)); | |
for(local i = 0; i < NUMPIXELS; i++) | |
{ | |
local colour = NIGHTCOLOUR; | |
if ((i >= rise)&&(i <= down)) | |
{ | |
colour = sinFade(i, DAYCOLOUR); | |
} | |
logToServer(format("[%d, %d, %d]", colour[0], colour[1], colour[2])); | |
setPixel(i, colour); | |
} | |
setPixel(now, RED); | |
} | |
} | |
class Temperature extends Mode | |
{ | |
temp = null; | |
TEMPCOLOUR = null; | |
constructor() | |
{ | |
base.constructor(); | |
temp = 0; | |
TEMPCOLOUR = RED; | |
name = "Temperature"; | |
} | |
function update() | |
{ | |
for(local i = 0; i < NUMPIXELS; i++) | |
{ | |
local colour = BLACK; | |
if (i <= temp) | |
{ | |
colour = ((i % 5 == 0) && (i != 0)) ? WHITE : TEMPCOLOUR; | |
} | |
setPixel(i, colour); | |
} | |
} | |
} | |
class Fader extends Mode | |
{ | |
genColour = null; | |
constructor() | |
{ | |
base.constructor(); | |
name = "Fader"; | |
genColour = getFromColourWheel(); | |
} | |
function update() | |
{ | |
local colour = resume genColour; | |
pixels.pop(); | |
pixels.insert(0, colour); | |
} | |
} | |
class Waiting extends Mode | |
{ | |
colours = null; | |
offset = null; | |
direction = null; | |
constructor() | |
{ | |
base.constructor(); | |
colours = [RED, YELLOW, GREEN, BLUE]; | |
offset = 0; | |
direction = 0; | |
name = "Waiting" | |
} | |
function update() | |
{ | |
for(local i = 0; i < offset; i++) | |
{ | |
local px1 = (((direction * 15 + i) % 60) + 60) % 60; | |
local px2 = (((direction * 15 - i) % 60) + 60) % 60; | |
setPixel(px1, colours[direction]); | |
setPixel(px2, colours[direction]); | |
if(offset == 32) | |
{ | |
direction = (direction + 1) % 4; | |
offset = 0; | |
} | |
} | |
offset++; | |
} | |
} | |
function addMode(modeName) | |
{ | |
modes.push(modeName); | |
} | |
function setMode() | |
{ | |
modes[current_mode].draw(); | |
if(demo) demoMode(); | |
} | |
function demoMode() | |
{ | |
local timeSec = date(time()).sec | |
if((timeSec % 10 == 0) && (demoTime != timeSec)) | |
{ | |
demoTime = timeSec | |
current_mode = (current_mode++) % modes.len(); | |
} | |
} | |
function sendNames() { | |
local names = []; | |
foreach(mode in modes) | |
{ | |
names.push(mode.name); | |
} | |
agent.send("setModes", names); | |
} | |
function setCurrentMode(mode) | |
{ | |
current_mode = mode; | |
logToServer("Set mode: " + current_mode + " : " + modes[mode].name); | |
} | |
function changeWindmill(data) | |
{ | |
modes.remove(data.i); | |
modes.insert(data.i, Windmill(data.segments, RED)); | |
} | |
function setFillClock(mode) | |
{ | |
logToServer(format("Set fillclock %s", mode ? "true" : "false")); | |
modes[0].fillClock = mode; | |
} | |
function setDebug(mode) | |
{ | |
if((mode == true)||(mode == false)) debug = mode; | |
} | |
function setDemo(mode) | |
{ | |
if((mode == true)||(mode == false)) demo = mode; | |
} | |
function setDelay(delay) | |
{ | |
delay = delay; | |
} | |
function setSun(data) | |
{ | |
modes[data.i].rise = data.rise; | |
modes[data.i].down = data.down; | |
} | |
function setTemp(data) | |
{ | |
modes[data.i].temp = data.temp; | |
} | |
agent.on("mode", setCurrentMode); | |
agent.on("fillclock", setFillClock); | |
agent.on("debug", setDebug); | |
agent.on("demo", setDemo); | |
agent.on("sun", setSun); | |
agent.on("temp", setTemp); | |
agent.on("windmill", changeWindmill); | |
agent.on("delay", setDelay); | |
modes <- []; | |
current_mode <- 0; | |
debug <- false; | |
demo <- false; | |
delay <- 0.001; | |
demoTime <- 1; | |
addMode(Clock()); //0 | |
addMode(Chase()); //1 | |
addMode(Rainbow()); //2 | |
addMode(Pac()); //3 | |
addMode(Windmill(4, RED)); //4 | |
addMode(Kitt(6)); //5 | |
addMode(Pulse(GREEN, RED)); //6 | |
addMode(Sunlight()); //7 | |
addMode(Temperature()); //8 | |
addMode(Fader()); //9 | |
addMode(Waiting()); //10 | |
setMode(); | |
sendNames(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment