comparison service/arduinoNode/devices.py @ 1425:75aaa0547d55

whitespace Ignore-this: 5ebd8b301f22adecc8c34b9e28aa8c76 darcs-hash:bfce337ecbc8660031d58be7e268dc804e16ad8a
author drewp <drewp@bigasterisk.com>
date Wed, 07 Aug 2019 21:06:04 -0700
parents 458355ee1b99
children 6b80a6c58907
comparison
equal deleted inserted replaced
1424:458355ee1b99 1425:75aaa0547d55
15 def readLine(read): 15 def readLine(read):
16 buf = '' 16 buf = ''
17 for c in iter(lambda: read(1), '\n'): 17 for c in iter(lambda: read(1), '\n'):
18 buf += c 18 buf += c
19 return buf 19 return buf
20 20
21 class DeviceType(object): 21 class DeviceType(object):
22 deviceType = None 22 deviceType = None
23 @classmethod 23 @classmethod
24 def findInstances(cls, graph, board): 24 def findInstances(cls, graph, board):
25 """ 25 """
64 'pinNumber': getattr(self, 'pinNumber', None), 64 'pinNumber': getattr(self, 'pinNumber', None),
65 'outputPatterns': self.outputPatterns(), 65 'outputPatterns': self.outputPatterns(),
66 'watchPrefixes': self.watchPrefixes(), 66 'watchPrefixes': self.watchPrefixes(),
67 'outputWidgets': self.outputWidgets(), 67 'outputWidgets': self.outputWidgets(),
68 } 68 }
69 69
70 def readFromPoll(self, read): 70 def readFromPoll(self, read):
71 """ 71 """
72 read an update message returned as part of a poll bundle. This may 72 read an update message returned as part of a poll bundle. This may
73 consume a varying number of bytes depending on the type of 73 consume a varying number of bytes depending on the type of
74 input (e.g. IR receiver). 74 input (e.g. IR receiver).
76 """ 76 """
77 raise NotImplementedError('readFromPoll in %s' % self.__class__) 77 raise NotImplementedError('readFromPoll in %s' % self.__class__)
78 78
79 def wantIdleOutput(self): 79 def wantIdleOutput(self):
80 return False 80 return False
81 81
82 def outputIdle(self, write): 82 def outputIdle(self, write):
83 return 83 return
84 84
85 def hostStatements(self): 85 def hostStatements(self):
86 """ 86 """
87 Like readFromPoll but these statements come from the host-side 87 Like readFromPoll but these statements come from the host-side
88 python code, not the connected device. Include output state 88 python code, not the connected device. Include output state
89 (e.g. light brightness) if its master version is in this 89 (e.g. light brightness) if its master version is in this
90 object. This method is called on /graph requests so it should 90 object. This method is called on /graph requests so it should
91 be fast. 91 be fast.
92 """ 92 """
93 return [] 93 return []
94 94
95 def watchPrefixes(self): 95 def watchPrefixes(self):
96 """ 96 """
97 subj,pred pairs of the statements that might be returned from 97 subj,pred pairs of the statements that might be returned from
98 readFromPoll, so the dashboard knows what it should 98 readFromPoll, so the dashboard knows what it should
99 watch. This should be eliminated, as the dashboard should just 99 watch. This should be eliminated, as the dashboard should just
100 always watch the whole tree of statements starting self.uri 100 always watch the whole tree of statements starting self.uri
101 """ 101 """
102 return [] 102 return []
103 103
104 def generateIncludes(self): 104 def generateIncludes(self):
105 """filenames of .h files to #include""" 105 """filenames of .h files to #include"""
106 return [] 106 return []
107 107
108 def generateArduinoLibs(self): 108 def generateArduinoLibs(self):
109 """names of libraries for the ARDUINO_LIBS line in the makefile""" 109 """names of libraries for the ARDUINO_LIBS line in the makefile"""
110 return [] 110 return []
111 111
112 def generateGlobalCode(self): 112 def generateGlobalCode(self):
113 """C code to emit in the global section. 113 """C code to emit in the global section.
114 114
115 Note that 'frame' (uint8) is available and increments each frame. 115 Note that 'frame' (uint8) is available and increments each frame.
116 """ 116 """
117 return '' 117 return ''
118 118
119 def generateSetupCode(self): 119 def generateSetupCode(self):
120 """C code to emit in setup()""" 120 """C code to emit in setup()"""
121 return '' 121 return ''
122 122
123 def generateIdleCode(self): 123 def generateIdleCode(self):
124 """C code to emit in the serial-read loop""" 124 """C code to emit in the serial-read loop"""
125 return '' 125 return ''
126 126
127 def generatePollCode(self): 127 def generatePollCode(self):
128 """ 128 """
129 C code to run a poll update. This should Serial.write its output 129 C code to run a poll update. This should Serial.write its output
130 for readFromPoll to consume. If this returns nothing, we don't 130 for readFromPoll to consume. If this returns nothing, we don't
131 try to poll this device. 131 try to poll this device.
138 board to receive whatever sendOutput writes. Each sendOutput 138 board to receive whatever sendOutput writes. Each sendOutput
139 write(buf) call should be matched with len(buf) Serial.read() 139 write(buf) call should be matched with len(buf) Serial.read()
140 calls in here. 140 calls in here.
141 """ 141 """
142 return '' 142 return ''
143 143
144 def outputPatterns(self): 144 def outputPatterns(self):
145 """ 145 """
146 Triple patterns, using None as a wildcard, that should be routed 146 Triple patterns, using None as a wildcard, that should be routed
147 to sendOutput 147 to sendOutput
148 """ 148 """
152 """ 152 """
153 structs to make output widgets on the dashboard. ~1 of these per 153 structs to make output widgets on the dashboard. ~1 of these per
154 handler you have in sendOutput 154 handler you have in sendOutput
155 """ 155 """
156 return [] 156 return []
157 157
158 def sendOutput(self, statements, write, read): 158 def sendOutput(self, statements, write, read):
159 """ 159 """
160 If we got statements that match this class's outputPatterns, this 160 If we got statements that match this class's outputPatterns, this
161 will be called with the statements that matched, and a serial 161 will be called with the statements that matched, and a serial
162 write method. What you write here will be available as 162 write method. What you write here will be available as
164 164
165 Todo: it would be fine to read back confirmations or 165 Todo: it would be fine to read back confirmations or
166 whatever. Just need a way to collect them into graph statements. 166 whatever. Just need a way to collect them into graph statements.
167 """ 167 """
168 raise NotImplementedError 168 raise NotImplementedError
169 169
170 _knownTypes = set() 170 _knownTypes = set()
171 def register(deviceType): 171 def register(deviceType):
172 _knownTypes.add(deviceType) 172 _knownTypes.add(deviceType)
173 return deviceType 173 return deviceType
174 174
175 @register 175 @register
176 class PingInput(DeviceType): 176 class PingInput(DeviceType):
177 @classmethod 177 @classmethod
178 def findInstances(cls, graph, board): 178 def findInstances(cls, graph, board):
179 return [cls(graph, board, None)] 179 return [cls(graph, board, None)]
180 180
181 def generatePollCode(self): 181 def generatePollCode(self):
182 return "Serial.write('k');" 182 return "Serial.write('k');"
183 183
184 def readFromPoll(self, read): 184 def readFromPoll(self, read):
185 byte = read(1) 185 byte = read(1)
186 if byte != 'k': 186 if byte != 'k':
187 raise ValueError('invalid ping response: chr(%s)' % ord(byte)) 187 raise ValueError('invalid ping response: chr(%s)' % ord(byte))
188 return [(self.uri, ROOM['ping'], ROOM['ok'])] 188 return [(self.uri, ROOM['ping'], ROOM['ok'])]
194 class MotionSensorInput(DeviceType): 194 class MotionSensorInput(DeviceType):
195 deviceType = ROOM['MotionSensor'] 195 deviceType = ROOM['MotionSensor']
196 def __init__(self, graph, uri, pinNumber): 196 def __init__(self, graph, uri, pinNumber):
197 DeviceType.__init__(self, graph, uri, pinNumber) 197 DeviceType.__init__(self, graph, uri, pinNumber)
198 self.lastRead = None 198 self.lastRead = None
199 199
200 def generateSetupCode(self): 200 def generateSetupCode(self):
201 return 'pinMode(%(pin)d, INPUT); digitalWrite(%(pin)d, LOW);' % { 201 return 'pinMode(%(pin)d, INPUT); digitalWrite(%(pin)d, LOW);' % {
202 'pin': self.pinNumber, 202 'pin': self.pinNumber,
203 } 203 }
204 204
205 def generatePollCode(self): 205 def generatePollCode(self):
206 return "Serial.write(digitalRead(%(pin)d) ? 'y' : 'n');" % { 206 return "Serial.write(digitalRead(%(pin)d) ? 'y' : 'n');" % {
207 'pin': self.pinNumber 207 'pin': self.pinNumber
208 } 208 }
209 209
210 def readFromPoll(self, read): 210 def readFromPoll(self, read):
211 b = read(1) 211 b = read(1)
212 if b not in 'yn': 212 if b not in 'yn':
213 raise ValueError('unexpected response %r' % b) 213 raise ValueError('unexpected response %r' % b)
214 motion = b == 'y' 214 motion = b == 'y'
215 215
216 oneshot = [] 216 oneshot = []
217 if self.lastRead is not None and motion != self.lastRead: 217 if self.lastRead is not None and motion != self.lastRead:
218 oneshot = [(self.uri, ROOM['sees'], ROOM['motionStart'])] 218 oneshot = [(self.uri, ROOM['sees'], ROOM['motionStart'])]
219 self.lastRead = motion 219 self.lastRead = motion
220 220
221 return {'latest': [ 221 return {'latest': [
222 (self.uri, ROOM['sees'], 222 (self.uri, ROOM['sees'],
223 ROOM['motion'] if motion else ROOM['noMotion']), 223 ROOM['motion'] if motion else ROOM['noMotion']),
224 ] + self.recentMotionStatements(motion), 224 ] + self.recentMotionStatements(motion),
225 'oneshot': oneshot} 225 'oneshot': oneshot}
233 dt = now - self.lastMotionTime 233 dt = now - self.lastMotionTime
234 return [(self.uri, ROOM['seesRecently'], 234 return [(self.uri, ROOM['seesRecently'],
235 ROOM['motion'] if (dt < 60 * 10) else ROOM['noMotion']), 235 ROOM['motion'] if (dt < 60 * 10) else ROOM['noMotion']),
236 (self.uri, ROOM['seesRecently30'], 236 (self.uri, ROOM['seesRecently30'],
237 ROOM['motion'] if (dt < 30) else ROOM['noMotion'])] 237 ROOM['motion'] if (dt < 30) else ROOM['noMotion'])]
238 238
239 def watchPrefixes(self): 239 def watchPrefixes(self):
240 return [ 240 return [
241 (self.uri, ROOM['sees']), 241 (self.uri, ROOM['sees']),
242 (self.uri, ROOM['seesRecently']), 242 (self.uri, ROOM['seesRecently']),
243 (self.uri, ROOM['seesRecently30']), 243 (self.uri, ROOM['seesRecently30']),
248 """add a switch to ground; we'll turn on pullup""" 248 """add a switch to ground; we'll turn on pullup"""
249 deviceType = ROOM['Pushbutton'] 249 deviceType = ROOM['Pushbutton']
250 def __init__(self, graph, uri, pinNumber): 250 def __init__(self, graph, uri, pinNumber):
251 DeviceType.__init__(self, graph, uri, pinNumber) 251 DeviceType.__init__(self, graph, uri, pinNumber)
252 self.lastClosed = None 252 self.lastClosed = None
253 253
254 def generateSetupCode(self): 254 def generateSetupCode(self):
255 return 'pinMode(%(pin)d, INPUT); digitalWrite(%(pin)d, HIGH);' % { 255 return 'pinMode(%(pin)d, INPUT); digitalWrite(%(pin)d, HIGH);' % {
256 'pin': self.pinNumber, 256 'pin': self.pinNumber,
257 } 257 }
258 258
259 def generatePollCode(self): 259 def generatePollCode(self):
260 # note: pulldown means unpressed reads as a 1 260 # note: pulldown means unpressed reads as a 1
261 return "Serial.write(digitalRead(%(pin)d) ? '0' : '1');" % { 261 return "Serial.write(digitalRead(%(pin)d) ? '0' : '1');" % {
262 'pin': self.pinNumber 262 'pin': self.pinNumber
263 } 263 }
264 264
265 def readFromPoll(self, read): 265 def readFromPoll(self, read):
266 b = read(1) 266 b = read(1)
267 if b not in '01': 267 if b not in '01':
268 raise ValueError('unexpected response %r' % b) 268 raise ValueError('unexpected response %r' % b)
269 closed = b == '0' 269 closed = b == '0'
274 ROOM['press'] if closed else ROOM['release']), 274 ROOM['press'] if closed else ROOM['release']),
275 ] 275 ]
276 else: 276 else:
277 oneshot = [] 277 oneshot = []
278 self.lastClosed = closed 278 self.lastClosed = closed
279 279
280 return {'latest': [ 280 return {'latest': [
281 (self.uri, ROOM['buttonState'], 281 (self.uri, ROOM['buttonState'],
282 ROOM['pressed'] if closed else ROOM['notPressed']), 282 ROOM['pressed'] if closed else ROOM['notPressed']),
283 ], 283 ],
284 'oneshot': oneshot} 284 'oneshot': oneshot}
285 285
286 def watchPrefixes(self): 286 def watchPrefixes(self):
287 return [ 287 return [
288 (self.uri, ROOM['buttonState']), 288 (self.uri, ROOM['buttonState']),
289 ] 289 ]
290 290
296 statements for all devices we find, even if we don't scan them, so 296 statements for all devices we find, even if we don't scan them, so
297 you can more easily add them to your config. Onewire search 297 you can more easily add them to your config. Onewire search
298 happens only at device startup (not even program startup, yet). 298 happens only at device startup (not even program startup, yet).
299 299
300 self.uri is a resource representing the bus. 300 self.uri is a resource representing the bus.
301 301
302 DS18S20 pin 1: ground, pin 2: data and pull-up with 4.7k. 302 DS18S20 pin 1: ground, pin 2: data and pull-up with 4.7k.
303 """ 303 """
304 deviceType = ROOM['OneWire'] 304 deviceType = ROOM['OneWire']
305 pollPeriod = 2 305 pollPeriod = 2
306 def hostStateInit(self): 306 def hostStateInit(self):
309 def generateIncludes(self): 309 def generateIncludes(self):
310 return ['OneWire.h', 'DallasTemperature.h'] 310 return ['OneWire.h', 'DallasTemperature.h']
311 311
312 def generateArduinoLibs(self): 312 def generateArduinoLibs(self):
313 return ['OneWire', 'DallasTemperature'] 313 return ['OneWire', 'DallasTemperature']
314 314
315 def generateGlobalCode(self): 315 def generateGlobalCode(self):
316 # not yet isolated to support multiple OW buses 316 # not yet isolated to support multiple OW buses
317 return ''' 317 return '''
318 OneWire oneWire(%(pinNumber)s); 318 OneWire oneWire(%(pinNumber)s);
319 DallasTemperature sensors(&oneWire); 319 DallasTemperature sensors(&oneWire);
320 #define MAX_DEVICES 8 320 #define MAX_DEVICES 8
321 DeviceAddress tempSensorAddress[MAX_DEVICES]; 321 DeviceAddress tempSensorAddress[MAX_DEVICES];
322 322
323 void initSensors() { 323 void initSensors() {
324 sensors.begin(); 324 sensors.begin();
325 sensors.setResolution(12); 325 sensors.setResolution(12);
326 sensors.setWaitForConversion(false); 326 sensors.setWaitForConversion(false);
327 for (uint8_t i=0; i < sensors.getDeviceCount(); ++i) { 327 for (uint8_t i=0; i < sensors.getDeviceCount(); ++i) {
328 sensors.getAddress(tempSensorAddress[i], i); 328 sensors.getAddress(tempSensorAddress[i], i);
329 } 329 }
330 } 330 }
331 ''' % dict(pinNumber=self.pinNumber) 331 ''' % dict(pinNumber=self.pinNumber)
332 332
333 def generateSetupCode(self): 333 def generateSetupCode(self):
334 return 'initSensors();' 334 return 'initSensors();'
335 335
336 def generatePollCode(self): 336 def generatePollCode(self):
337 return r''' 337 return r'''
338 sensors.requestTemperatures(); 338 sensors.requestTemperatures();
339 339
340 // If we need frequent idle calls or fast polling again, this needs 340 // If we need frequent idle calls or fast polling again, this needs
344 sensors.setWaitForConversion(true); // ~100ms 344 sensors.setWaitForConversion(true); // ~100ms
345 345
346 Serial.write((uint8_t)sensors.getDeviceCount()); 346 Serial.write((uint8_t)sensors.getDeviceCount());
347 for (uint8_t i=0; i < sensors.getDeviceCount(); ++i) { 347 for (uint8_t i=0; i < sensors.getDeviceCount(); ++i) {
348 float newTemp = sensors.getTempF(tempSensorAddress[i]); 348 float newTemp = sensors.getTempF(tempSensorAddress[i]);
349 349
350 Serial.write(tempSensorAddress[i], 8); 350 Serial.write(tempSensorAddress[i], 8);
351 Serial.write((uint8_t*)(&newTemp), 4); 351 Serial.write((uint8_t*)(&newTemp), 4);
352 } 352 }
353 ''' 353 '''
354 354
389 @register 389 @register
390 class LedOutput(DeviceType): 390 class LedOutput(DeviceType):
391 deviceType = ROOM['LedOutput'] 391 deviceType = ROOM['LedOutput']
392 def hostStateInit(self): 392 def hostStateInit(self):
393 self.value = 0 393 self.value = 0
394 394
395 def generateSetupCode(self): 395 def generateSetupCode(self):
396 return 'pinMode(%(pin)d, OUTPUT); digitalWrite(%(pin)d, LOW);' % { 396 return 'pinMode(%(pin)d, OUTPUT); digitalWrite(%(pin)d, LOW);' % {
397 'pin': self.pinNumber, 397 'pin': self.pinNumber,
398 } 398 }
399 399
400 def outputPatterns(self): 400 def outputPatterns(self):
401 return [(self.uri, ROOM['brightness'], None)] 401 return [(self.uri, ROOM['brightness'], None)]
402 402
403 def sendOutput(self, statements, write, read): 403 def sendOutput(self, statements, write, read):
404 assert len(statements) == 1 404 assert len(statements) == 1
408 self.value = 1 - self.value 408 self.value = 1 - self.value
409 write(byteFromFloat(self.value)) 409 write(byteFromFloat(self.value))
410 410
411 def hostStatements(self): 411 def hostStatements(self):
412 return [(self.uri, ROOM['brightness'], Literal(self.value))] 412 return [(self.uri, ROOM['brightness'], Literal(self.value))]
413 413
414 def generateActionCode(self): 414 def generateActionCode(self):
415 return r''' 415 return r'''
416 while(Serial.available() < 1) NULL; 416 while(Serial.available() < 1) NULL;
417 analogWrite(%(pin)d, Serial.read()); 417 analogWrite(%(pin)d, Serial.read());
418 ''' % dict(pin=self.pinNumber) 418 ''' % dict(pin=self.pinNumber)
430 @register 430 @register
431 class DigitalOutput(DeviceType): 431 class DigitalOutput(DeviceType):
432 deviceType = ROOM['DigitalOutput'] 432 deviceType = ROOM['DigitalOutput']
433 def hostStateInit(self): 433 def hostStateInit(self):
434 self.value = 0 434 self.value = 0
435 435
436 def generateSetupCode(self): 436 def generateSetupCode(self):
437 return 'pinMode(%(pin)d, OUTPUT); digitalWrite(%(pin)d, LOW);' % { 437 return 'pinMode(%(pin)d, OUTPUT); digitalWrite(%(pin)d, LOW);' % {
438 'pin': self.pinNumber, 438 'pin': self.pinNumber,
439 } 439 }
440 440
441 def outputPatterns(self): 441 def outputPatterns(self):
442 return [(self.uri, ROOM['level'], None)] 442 return [(self.uri, ROOM['level'], None)]
443 443
444 def sendOutput(self, statements, write, read): 444 def sendOutput(self, statements, write, read):
445 assert len(statements) == 1 445 assert len(statements) == 1
448 write(chr(self.value)) 448 write(chr(self.value))
449 449
450 def hostStatements(self): 450 def hostStatements(self):
451 return [(self.uri, ROOM['level'], 451 return [(self.uri, ROOM['level'],
452 Literal('high' if self.value else 'low'))] 452 Literal('high' if self.value else 'low'))]
453 453
454 def generateActionCode(self): 454 def generateActionCode(self):
455 return r''' 455 return r'''
456 while(Serial.available() < 1) NULL; 456 while(Serial.available() < 1) NULL;
457 digitalWrite(%(pin)d, Serial.read()); 457 digitalWrite(%(pin)d, Serial.read());
458 ''' % dict(pin=self.pinNumber) 458 ''' % dict(pin=self.pinNumber)
459 459
460 def outputWidgets(self): 460 def outputWidgets(self):
461 return [{ 461 return [{
462 'element': 'output-switch', 462 'element': 'output-switch',
463 'subj': self.uri, 463 'subj': self.uri,
464 'pred': ROOM['level'], 464 'pred': ROOM['level'],
465 }] 465 }]
466 466
467 467
468 @register 468 @register
469 class PwmBoard(DeviceType): 469 class PwmBoard(DeviceType):
470 deviceType = ROOM['PwmBoard'] 470 deviceType = ROOM['PwmBoard']
471 @classmethod 471 @classmethod
472 def findInstances(cls, graph, board): 472 def findInstances(cls, graph, board):
485 for out in graph.query("""SELECT DISTINCT ?area ?chan WHERE { 485 for out in graph.query("""SELECT DISTINCT ?area ?chan WHERE {
486 ?dev :output [:area ?area; :channel ?chan] . 486 ?dev :output [:area ?area; :channel ?chan] .
487 }""", initBindings=dict(dev=row.dev), initNs={'': ROOM}): 487 }""", initBindings=dict(dev=row.dev), initNs={'': ROOM}):
488 outs[out.area] = out.chan.toPython() 488 outs[out.area] = out.chan.toPython()
489 yield cls(graph, row.dev, outs=outs) 489 yield cls(graph, row.dev, outs=outs)
490 490
491 def __init__(self, graph, dev, outs): 491 def __init__(self, graph, dev, outs):
492 self.codeVals = {'pwm': 'pwm%s' % (hash(str(dev)) % 99999)} 492 self.codeVals = {'pwm': 'pwm%s' % (hash(str(dev)) % 99999)}
493 self.outs = outs 493 self.outs = outs
494 super(PwmBoard, self).__init__(graph, dev, pinNumber=None) 494 super(PwmBoard, self).__init__(graph, dev, pinNumber=None)
495 495
497 self.values = {uri: 0 for uri in self.outs.keys()} # uri: brightness 497 self.values = {uri: 0 for uri in self.outs.keys()} # uri: brightness
498 498
499 def hostStatements(self): 499 def hostStatements(self):
500 return [(uri, ROOM['brightness'], Literal(b)) 500 return [(uri, ROOM['brightness'], Literal(b))
501 for uri, b in self.values.items()] 501 for uri, b in self.values.items()]
502 502
503 def generateIncludes(self): 503 def generateIncludes(self):
504 return ['Wire.h', 'Adafruit_PWMServoDriver.h'] 504 return ['Wire.h', 'Adafruit_PWMServoDriver.h']
505 505
506 def generateArduinoLibs(self): 506 def generateArduinoLibs(self):
507 return ['Wire', 'Adafruit-PWM-Servo-Driver-Library'] 507 return ['Wire', 'Adafruit-PWM-Servo-Driver-Library']
508 508
509 def generateGlobalCode(self): 509 def generateGlobalCode(self):
510 return r''' 510 return r'''
511 Adafruit_PWMServoDriver %(pwm)s = Adafruit_PWMServoDriver(0x40); 511 Adafruit_PWMServoDriver %(pwm)s = Adafruit_PWMServoDriver(0x40);
512 ''' % self.codeVals 512 ''' % self.codeVals
513 513
514 def generateSetupCode(self): 514 def generateSetupCode(self):
515 return ''' 515 return '''
516 %(pwm)s.begin(); 516 %(pwm)s.begin();
517 %(pwm)s.setPWMFreq(1200); 517 %(pwm)s.setPWMFreq(1200);
518 ''' % self.codeVals 518 ''' % self.codeVals
519 519
520 def generateActionCode(self): 520 def generateActionCode(self):
521 return r''' 521 return r'''
522 while(Serial.available() < 3) NULL; 522 while(Serial.available() < 3) NULL;
523 byte chan = Serial.read(); 523 byte chan = Serial.read();
524 uint16_t level = uint16_t(Serial.read()) << 8; 524 uint16_t level = uint16_t(Serial.read()) << 8;
535 chan = self.outs[statements[0][0]] 535 chan = self.outs[statements[0][0]]
536 value = float(statements[0][2]) 536 value = float(statements[0][2])
537 self.values[statements[0][0]] = value 537 self.values[statements[0][0]] = value
538 v12 = int(min(4095, max(0, value * 4095))) 538 v12 = int(min(4095, max(0, value * 4095)))
539 write(chr(chan) + chr(v12 >> 8) + chr(v12 & 0xff)) 539 write(chr(chan) + chr(v12 >> 8) + chr(v12 & 0xff))
540 540
541 def outputWidgets(self): 541 def outputWidgets(self):
542 return [{ 542 return [{
543 'element': 'output-slider', 543 'element': 'output-slider',
544 'min': 0, 544 'min': 0,
545 'max': 1, 545 'max': 1,
578 def generateIncludes(self): 578 def generateIncludes(self):
579 return ['ST7565.h'] 579 return ['ST7565.h']
580 580
581 def generateArduinoLibs(self): 581 def generateArduinoLibs(self):
582 return ['ST7565'] 582 return ['ST7565']
583 583
584 def generateGlobalCode(self): 584 def generateGlobalCode(self):
585 return ''' 585 return '''
586 ST7565 glcd(%(SID)d, %(SCLK)d, %(A0)d, %(RST)d, %(CS)d); 586 ST7565 glcd(%(SID)d, %(SCLK)d, %(A0)d, %(RST)d, %(CS)d);
587 char newtxt[21*8+1]; 587 char newtxt[21*8+1];
588 unsigned int written; 588 unsigned int written;
589 ''' % dict(SID=self.connections[ROOM['lcdSID']], 589 ''' % dict(SID=self.connections[ROOM['lcdSID']],
590 SCLK=self.connections[ROOM['lcdSCLK']], 590 SCLK=self.connections[ROOM['lcdSCLK']],
591 A0=self.connections[ROOM['lcdA0']], 591 A0=self.connections[ROOM['lcdA0']],
592 RST=self.connections[ROOM['lcdRST']], 592 RST=self.connections[ROOM['lcdRST']],
593 CS=self.connections[ROOM['lcdCS']]) 593 CS=self.connections[ROOM['lcdCS']])
594 594
595 def generateSetupCode(self): 595 def generateSetupCode(self):
596 return ''' 596 return '''
597 glcd.st7565_init(); 597 glcd.st7565_init();
598 glcd.st7565_command(CMD_DISPLAY_ON); 598 glcd.st7565_command(CMD_DISPLAY_ON);
599 glcd.st7565_command(CMD_SET_ALLPTS_NORMAL); 599 glcd.st7565_command(CMD_SET_ALLPTS_NORMAL);
600 glcd.st7565_set_brightness(0x18); 600 glcd.st7565_set_brightness(0x18);
601 601
602 glcd.display(); // show splashscreen 602 glcd.display(); // show splashscreen
603 ''' 603 '''
604 604
605 def outputPatterns(self): 605 def outputPatterns(self):
606 return [(self.uri, ROOM['text'], None)] 606 return [(self.uri, ROOM['text'], None)]
612 assert len(self.text) < 254, repr(self.text) 612 assert len(self.text) < 254, repr(self.text)
613 write(chr(len(self.text)) + self.text) 613 write(chr(len(self.text)) + self.text)
614 614
615 def hostStatements(self): 615 def hostStatements(self):
616 return [(self.uri, ROOM['text'], Literal(self.text))] 616 return [(self.uri, ROOM['text'], Literal(self.text))]
617 617
618 def outputWidgets(self): 618 def outputWidgets(self):
619 return [{ 619 return [{
620 'element': 'output-fixed-text', 620 'element': 'output-fixed-text',
621 'cols': 21, 621 'cols': 21,
622 'rows': 8, 622 'rows': 8,
623 'subj': self.uri, 623 'subj': self.uri,
624 'pred': ROOM['text'], 624 'pred': ROOM['text'],
625 }] 625 }]
626 626
627 def generateActionCode(self): 627 def generateActionCode(self):
628 return ''' 628 return '''
629 while(Serial.available() < 1) NULL; 629 while(Serial.available() < 1) NULL;
630 byte bufSize = Serial.read(); 630 byte bufSize = Serial.read();
631 for (byte i = 0; i < bufSize; ++i) { 631 for (byte i = 0; i < bufSize; ++i) {
634 } 634 }
635 for (byte i = bufSize; i < sizeof(newtxt); ++i) { 635 for (byte i = bufSize; i < sizeof(newtxt); ++i) {
636 newtxt[i] = 0; 636 newtxt[i] = 0;
637 } 637 }
638 glcd.clear(); 638 glcd.clear();
639 glcd.drawstring(0,0, newtxt); 639 glcd.drawstring(0,0, newtxt);
640 glcd.display(); 640 glcd.display();
641 ''' 641 '''
642 642
643 @register 643 @register
644 class RgbPixels(DeviceType): 644 class RgbPixels(DeviceType):
646 deviceType = ROOM['RgbPixels'] 646 deviceType = ROOM['RgbPixels']
647 647
648 def __init__(self, graph, uri, pinNumber): 648 def __init__(self, graph, uri, pinNumber):
649 super(RgbPixels, self).__init__(graph, uri, pinNumber) 649 super(RgbPixels, self).__init__(graph, uri, pinNumber)
650 self.anim = RgbPixelsAnimation(graph, uri, self.updateOutput) 650 self.anim = RgbPixelsAnimation(graph, uri, self.updateOutput)
651 651
652 self.replace = {'ledArray': 'leds_%s' % self.pinNumber, 652 self.replace = {'ledArray': 'leds_%s' % self.pinNumber,
653 'ledCount': self.anim.maxIndex() - 1, 653 'ledCount': self.anim.maxIndex() - 1,
654 'pin': self.pinNumber, 654 'pin': self.pinNumber,
655 'ledType': 'WS2812', 655 'ledType': 'WS2812',
656 } 656 }
657 657
658 def generateIncludes(self): 658 def generateIncludes(self):
659 """filenames of .h files to #include""" 659 """filenames of .h files to #include"""
660 return ['FastLED.h'] 660 return ['FastLED.h']
661 661
662 def generateArduinoLibs(self): 662 def generateArduinoLibs(self):
663 """names of libraries for the ARDUINO_LIBS line in the makefile""" 663 """names of libraries for the ARDUINO_LIBS line in the makefile"""
664 return ['FastLED-3.1.0'] 664 return ['FastLED-3.1.0']
665 665
666 def myId(self): 666 def myId(self):
667 return 'rgb_%s' % self.pinNumber 667 return 'rgb_%s' % self.pinNumber
668 668
669 def generateGlobalCode(self): 669 def generateGlobalCode(self):
670 return 'CRGB {ledArray}[{ledCount}];'.format(**self.replace) 670 return 'CRGB {ledArray}[{ledCount}];'.format(**self.replace)
671 671
672 def generateSetupCode(self): 672 def generateSetupCode(self):
673 return 'FastLED.addLeds<{ledType}, {pin}>({ledArray}, {ledCount});'.format(**self.replace) 673 return 'FastLED.addLeds<{ledType}, {pin}>({ledArray}, {ledCount});'.format(**self.replace)
674 674
675 def sendOutput(self, statements, write, read): 675 def sendOutput(self, statements, write, read):
676 log.info('sendOutput start') 676 log.info('sendOutput start')
677 self.write = write 677 self.write = write
678 self.anim.onStatements(statements) 678 self.anim.onStatements(statements)
679 self.anim.updateOutput() 679 self.anim.updateOutput()
686 # my WS2812 need these flipped 686 # my WS2812 need these flipped
687 write(chr(idx) + chr(g) + chr(r) + chr(b)) 687 write(chr(idx) + chr(g) + chr(r) + chr(b))
688 688
689 def wantIdleOutput(self): 689 def wantIdleOutput(self):
690 return True 690 return True
691 691
692 def outputIdle(self, write): 692 def outputIdle(self, write):
693 self.write = write 693 self.write = write
694 self.updateOutput() 694 self.updateOutput()
695 695
696 def hostStatements(self): 696 def hostStatements(self):
697 return self.anim.hostStatements() 697 return self.anim.hostStatements()
698 698
699 def outputPatterns(self): 699 def outputPatterns(self):
700 return self.anim.outputPatterns() 700 return self.anim.outputPatterns()
701 701
702 def outputWidgets(self): 702 def outputWidgets(self):
703 return self.anim.outputWidgets() 703 return self.anim.outputWidgets()
717 while(Serial.available() < 1) NULL; 717 while(Serial.available() < 1) NULL;
718 byte g = Serial.read(); 718 byte g = Serial.read();
719 719
720 while(Serial.available() < 1) NULL; 720 while(Serial.available() < 1) NULL;
721 byte b = Serial.read(); 721 byte b = Serial.read();
722 722
723 {ledArray}[id] = CRGB(r, g, b); 723 {ledArray}[id] = CRGB(r, g, b);
724 }} 724 }}
725 FastLED.show(); 725 FastLED.show();
726 726
727 '''.format(**self.replace) 727 '''.format(**self.replace)
728 728
729 def makeDevices(graph, board): 729 def makeDevices(graph, board):
730 out = [] 730 out = []
731 for dt in sorted(_knownTypes, key=lambda cls: cls.__name__): 731 for dt in sorted(_knownTypes, key=lambda cls: cls.__name__):
732 out.extend(dt.findInstances(graph, board)) 732 out.extend(dt.findInstances(graph, board))
733 return out 733 return out
734