Mercurial > code > home > repos > homeauto
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 |