comparison service/arduinoNode/devices.py @ 974:f707210c13bd

new /output to post statements which devices can handle. led and lcd output working Ignore-this: afa16b081869a52380b04271a35c53c7 darcs-hash:20150412104414-312f9-f0e67d9f939025fd0a67e78463e6902ddcf0e6d9
author drewp <drewp@bigasterisk.com>
date Sun, 12 Apr 2015 03:44:14 -0700
parents fbe72d44f15a
children 376599552a4c
comparison
equal deleted inserted replaced
973:d7e8227fefc5 974:f707210c13bd
1 from __future__ import division
2 import itertools
1 from rdflib import Namespace, RDF, URIRef, Literal 3 from rdflib import Namespace, RDF, URIRef, Literal
2 4
3 ROOM = Namespace('http://projects.bigasterisk.com/room/') 5 ROOM = Namespace('http://projects.bigasterisk.com/room/')
6 XSD = Namespace('http://www.w3.org/2001/XMLSchema#')
4 7
5 def readLine(read): 8 def readLine(read):
6 buf = '' 9 buf = ''
7 for c in iter(lambda: read(1), '\n'): 10 for c in iter(lambda: read(1), '\n'):
8 buf += c 11 buf += c
16 return any number of instances of this class for all the separately 19 return any number of instances of this class for all the separately
17 controlled devices on the board. Two LEDS makes two instances, 20 controlled devices on the board. Two LEDS makes two instances,
18 but two sensors on the same onewire bus makes only one device 21 but two sensors on the same onewire bus makes only one device
19 (which yields more statements). 22 (which yields more statements).
20 """ 23 """
21 instances = []
22 for row in graph.query("""SELECT ?dev ?pinNumber WHERE { 24 for row in graph.query("""SELECT ?dev ?pinNumber WHERE {
23 ?board :hasPin ?pin . 25 ?board :hasPin ?pin .
24 ?pin :pinNumber ?pinNumber; 26 ?pin :pinNumber ?pinNumber;
25 :connectedTo ?dev . 27 :connectedTo ?dev .
26 ?dev a ?thisType . 28 ?dev a ?thisType .
27 } ORDER BY ?dev""", 29 } ORDER BY ?dev""",
28 initBindings=dict(board=board, 30 initBindings=dict(board=board,
29 thisType=cls.deviceType), 31 thisType=cls.deviceType),
30 initNs={'': ROOM}): 32 initNs={'': ROOM}):
31 instances.append(cls(graph, row.dev, int(row.pinNumber))) 33 yield cls(graph, row.dev, int(row.pinNumber))
32 return instances
33 34
34 # subclasses may add args to this 35 # subclasses may add args to this
35 def __init__(self, graph, uri, pinNumber): 36 def __init__(self, graph, uri, pinNumber):
36 self.graph, self.uri = graph, uri 37 self.graph, self.uri = graph, uri
37 self.pinNumber = pinNumber 38 self.pinNumber = pinNumber
61 """if this returns nothing, we don't try to poll this device""" 62 """if this returns nothing, we don't try to poll this device"""
62 return '' 63 return ''
63 64
64 def generateActionCode(self): 65 def generateActionCode(self):
65 """ 66 """
66 if you get called to do your action, this code reads the args you 67 If the host side runs sendOutput, this C code will be run on the
67 need and do the right action 68 board to receive whatever sendOutput writes. Each sendOutput
69 write(buf) call should be matched with len(buf) Serial.read()
70 calls in here.
68 """ 71 """
69 return '' 72 return ''
70 73
74 def outputPatterns(self):
75 """
76 Triple patterns, using None as a wildcard, that should be routed
77 to sendOutput
78 """
79 return []
80
81 def sendOutput(self, statements, write, read):
82 """
83 If we got statements that match this class's outputPatterns, this
84 will be called with the statements that matched, and a serial
85 write method. What you write here will be available as
86 Serial.read in the generateActionCode C code.
87
88 Todo: it would be fine to read back confirmations or
89 whatever. Just need a way to collect them into graph statements.
90 """
91 raise NotImplementedError
92
71 _knownTypes = set() 93 _knownTypes = set()
72 def register(deviceType): 94 def register(deviceType):
73 _knownTypes.add(deviceType) 95 _knownTypes.add(deviceType)
74 return deviceType 96 return deviceType
75 97
89 @register 111 @register
90 class MotionSensorInput(DeviceType): 112 class MotionSensorInput(DeviceType):
91 deviceType = ROOM['MotionSensor'] 113 deviceType = ROOM['MotionSensor']
92 def generateSetupCode(self): 114 def generateSetupCode(self):
93 return 'pinMode(%(pin)d, INPUT); digitalWrite(%(pin)d, LOW);' % { 115 return 'pinMode(%(pin)d, INPUT); digitalWrite(%(pin)d, LOW);' % {
94 'pin': self.pinNumber(), 116 'pin': self.pinNumber,
95 } 117 }
96 118
97 def generatePollCode(self): 119 def generatePollCode(self):
98 return "Serial.write(digitalRead(%(pin)d) ? 'y' : 'n');" % { 120 return "Serial.write(digitalRead(%(pin)d) ? 'y' : 'n');" % {
99 'pin': self.pinNumber() 121 'pin': self.pinNumber
100 } 122 }
101 123
102 def readFromPoll(self, read): 124 def readFromPoll(self, read):
103 b = read(1) 125 b = read(1)
104 if b not in 'yn': 126 if b not in 'yn':
121 # not yet isolated to support multiple OW buses 143 # not yet isolated to support multiple OW buses
122 return ''' 144 return '''
123 OneWire oneWire(%(pinNumber)s); 145 OneWire oneWire(%(pinNumber)s);
124 DallasTemperature sensors(&oneWire); 146 DallasTemperature sensors(&oneWire);
125 DeviceAddress tempSensorAddress; 147 DeviceAddress tempSensorAddress;
126 #define NUM_TEMPERATURE_RETRIES 5 148 #define NUM_TEMPERATURE_RETRIES 2
127 149
128 void initSensors() { 150 void initSensors() {
129 sensors.begin(); 151 sensors.begin();
130 sensors.getAddress(tempSensorAddress, 0); 152 sensors.getAddress(tempSensorAddress, 0);
131 sensors.setResolution(tempSensorAddress, 12); 153 sensors.setResolution(tempSensorAddress, 12);
132 } 154 }
133
134 ''' % dict(pinNumber=self.pinNumber) 155 ''' % dict(pinNumber=self.pinNumber)
156
135 def generatePollCode(self): 157 def generatePollCode(self):
136 return r''' 158 return r'''
137 for (int i=0; i<NUM_TEMPERATURE_RETRIES; i++) { 159 for (int i=0; i<NUM_TEMPERATURE_RETRIES; i++) {
138 sensors.requestTemperatures(); 160 sensors.requestTemperatures();
139 float newTemp = sensors.getTempF(tempSensorAddress); 161 float newTemp = sensors.getTempF(tempSensorAddress);
153 175
154 def readFromPoll(self, read): 176 def readFromPoll(self, read):
155 newTemp = readLine(read) 177 newTemp = readLine(read)
156 retries = ord(read(1)) 178 retries = ord(read(1))
157 return [ 179 return [
158 (self.uri, ROOM['temperatureF'], Literal(newTemp)), 180 (self.uri, ROOM['temperatureF'],
181 Literal(newTemp, datatype=XSD['decimal'])),
159 (self.uri, ROOM['temperatureRetries'], Literal(retries)), 182 (self.uri, ROOM['temperatureRetries'], Literal(retries)),
160 ] 183 ]
184
185 def byteFromFloat(f):
186 return chr(int(min(255, max(0, f * 255))))
187
188 @register
189 class LedOutput(DeviceType):
190 deviceType = ROOM['LedOutput']
191 def generateSetupCode(self):
192 return 'pinMode(%(pin)d, OUTPUT); digitalWrite(%(pin)d, LOW);' % {
193 'pin': self.pinNumber,
194 }
195
196 def outputPatterns(self):
197 return [(self.uri, ROOM['brightness'], None)]
198
199 def sendOutput(self, statements, write, read):
200 assert len(statements) == 1
201 assert statements[0][:2] == (self.uri, ROOM['brightness'])
202 value = float(statements[0][2])
203 if (self.uri, RDF.type, ROOM['ActiveLowOutput']):
204 value = 1 - value
205 write(byteFromFloat(value))
206
207 def generateActionCode(self):
208 return r'''
209 while(Serial.available() < 1) NULL;
210 analogWrite(%(pin)d, Serial.read());
211 ''' % dict(pin=self.pinNumber)
212
213 @register
214 class ST7576Lcd(DeviceType):
215 deviceType = ROOM['ST7565Lcd']
216 @classmethod
217 def findInstances(cls, graph, board):
218 grouped = itertools.groupby(
219 graph.query("""SELECT DISTINCT ?dev ?pred ?pinNumber WHERE {
220 ?board :hasPin ?pin .
221 ?pin :pinNumber ?pinNumber;
222 :connectedTo ?devPin .
223 ?dev a :ST7565Lcd .
224 ?dev ?pred ?devPin .
225 } ORDER BY ?dev""",
226 initBindings=dict(board=board,
227 thisType=cls.deviceType),
228 initNs={'': ROOM}),
229 lambda row: row.dev)
230 for dev, connections in grouped:
231 connections = dict((role, int(num)) for unused_dev, role, num
232 in connections)
233 yield cls(graph, dev, connections=connections)
234
235 def __init__(self, graph, dev, connections):
236 super(ST7576Lcd, self).__init__(graph, dev, pinNumber=None)
237 self.connections = connections
238
239 def generateIncludes(self):
240 return ['ST7565.h']
241
242 def generateArduinoLibs(self):
243 return ['ST7565']
244
245 def generateGlobalCode(self):
246 return '''
247 ST7565 glcd(%(SID)d, %(SCLK)d, %(A0)d, %(RST)d, %(CS)d);
248 char newtxt[21*8+1];
249 unsigned int written;
250 ''' % dict(SID=self.connections[ROOM['lcdSID']],
251 SCLK=self.connections[ROOM['lcdSCLK']],
252 A0=self.connections[ROOM['lcdA0']],
253 RST=self.connections[ROOM['lcdRST']],
254 CS=self.connections[ROOM['lcdCS']])
255
256 def generateSetupCode(self):
257 return '''
258 glcd.st7565_init();
259 glcd.st7565_command(CMD_DISPLAY_ON);
260 glcd.st7565_command(CMD_SET_ALLPTS_NORMAL);
261 glcd.st7565_set_brightness(0x18);
262
263 glcd.display(); // show splashscreen
264 '''
265
266 def outputPatterns(self):
267 return [(self.uri, ROOM['text'], None)]
268
269 def sendOutput(self, statements, write, read):
270 assert len(statements) == 1
271 assert statements[0][:2] == (self.uri, ROOM['text'])
272 value = str(statements[0][2])
273 assert len(value) < 254, repr(value)
274 write(chr(len(value)) + value)
275
276 def generateActionCode(self):
277 return '''
278 while(Serial.available() < 1) NULL;
279 byte bufSize = Serial.read();
280 for (byte i = 0; i < bufSize; i++) {
281 while(Serial.available() < 1) NULL;
282 newtxt[i] = Serial.read();
283 }
284 glcd.clear();
285 glcd.drawstring(0,0, newtxt);
286 glcd.display();
287 '''
161 288
162 def makeDevices(graph, board): 289 def makeDevices(graph, board):
163 out = [] 290 out = []
164 for dt in sorted(_knownTypes, key=lambda cls: cls.__name__): 291 for dt in sorted(_knownTypes, key=lambda cls: cls.__name__):
165 out.extend(dt.findInstances(graph, board)) 292 out.extend(dt.findInstances(graph, board))