diff service/arduinoNode/devices.py @ 971:fbe72d44f15a

only recompile if the C code is new. redo Device class api. single temperature sensor is working Ignore-this: e78106d25dbb2ac8c5e5d8a81d576358 darcs-hash:20150411084359-312f9-7c50d2f4f21dd9d5a0fa8913873a1e6d7d325118
author drewp <drewp@bigasterisk.com>
date Sat, 11 Apr 2015 01:43:59 -0700
parents 70a5392b24d3
children d228105749ac
line wrap: on
line diff
--- a/service/arduinoNode/devices.py	Sat Apr 11 01:43:14 2015 -0700
+++ b/service/arduinoNode/devices.py	Sat Apr 11 01:43:59 2015 -0700
@@ -1,14 +1,40 @@
-from rdflib import Namespace, RDF, URIRef
+from rdflib import Namespace, RDF, URIRef, Literal
 
 ROOM = Namespace('http://projects.bigasterisk.com/room/')
 
-class BoardInput(object):
-    """
-    one device that gives us input. this includes processing to make
-    statements, but this object doesn't store state
-    """
-    def __init__(self, graph, uri):
+def readLine(read):
+    buf = ''
+    for c in iter(lambda: read(1), '\n'):
+        buf += c
+    return buf
+            
+class DeviceType(object):
+    deviceType = None
+    @classmethod
+    def findInstances(cls, graph, board):
+        """
+        return any number of instances of this class for all the separately
+        controlled devices on the board. Two LEDS makes two instances,
+        but two sensors on the same onewire bus makes only one device
+        (which yields more statements).
+        """
+        instances = []
+        for row in graph.query("""SELECT ?dev ?pinNumber WHERE {
+                                    ?board :hasPin ?pin .
+                                    ?pin :pinNumber ?pinNumber;
+                                         :connectedTo ?dev .
+                                    ?dev a ?thisType .
+                                  } ORDER BY ?dev""",
+                               initBindings=dict(board=board,
+                                                 thisType=cls.deviceType),
+                               initNs={'': ROOM}):
+            instances.append(cls(graph, row.dev, int(row.pinNumber)))
+        return instances
+
+    # subclasses may add args to this
+    def __init__(self, graph, uri, pinNumber):
         self.graph, self.uri = graph, uri
+        self.pinNumber = pinNumber
         
     def readFromPoll(self, read):
         """
@@ -17,26 +43,42 @@
         input (e.g. IR receiver).
         Returns rdf statements.
         """
-        raise NotImplementedError
+        raise NotImplementedError('readFromPoll in %s' % self.__class__)
+
+    def generateIncludes(self):
+        return []
 
+    def generateArduinoLibs(self):
+        return []
+        
+    def generateGlobalCode(self):
+        return ''
+        
     def generateSetupCode(self):
         return ''
         
     def generatePollCode(self):
+        """if this returns nothing, we don't try to poll this device"""
         return ''
 
-    def pinNumber(self, pred=ROOM['pin']):
-        pinUri = self.graph.value(self.uri, pred)
-        return int(self.graph.value(pinUri, ROOM['pinNumber']))
+    def generateActionCode(self):
+        """
+        if you get called to do your action, this code reads the args you
+        need and do the right action
+        """
+        return ''
+       
+_knownTypes = set()
+def register(deviceType):
+    _knownTypes.add(deviceType)
+    return deviceType
 
-_inputForType = {}
-def registerInput(deviceType):
-    def newcls(cls):
-        _inputForType[deviceType] = cls
-        return cls
-    return newcls
-        
-class PingInput(BoardInput):
+@register
+class PingInput(DeviceType):
+    @classmethod
+    def findInstances(cls, graph, board):
+        return [cls(graph, board, None)]
+    
     def generatePollCode(self):
         return "Serial.write('k');"
     def readFromPoll(self, read):
@@ -44,8 +86,9 @@
             raise ValueError('invalid ping response')
         return [(self.uri, ROOM['ping'], ROOM['ok'])]
 
-@registerInput(deviceType=ROOM['MotionSensor'])
-class MotionSensorInput(BoardInput):
+@register
+class MotionSensorInput(DeviceType):
+    deviceType = ROOM['MotionSensor']
     def generateSetupCode(self):
         return 'pinMode(%(pin)d, INPUT); digitalWrite(%(pin)d, LOW);' % {
             'pin': self.pinNumber(),
@@ -64,7 +107,61 @@
         return [(self.uri, ROOM['sees'],
                  ROOM['motion'] if motion else ROOM['noMotion'])]
 
-def makeBoardInput(graph, uri):
-    deviceType = graph.value(uri, RDF.type)
-    return _inputForType[deviceType](graph, uri)
+@register
+class OneWire(DeviceType):
+    deviceType = ROOM['OneWire']
+   
+    def generateIncludes(self):
+        return ['OneWire.h', 'DallasTemperature.h']
+
+    def generateArduinoLibs(self):
+        return ['OneWire', 'DallasTemperature']
+        
+    def generateGlobalCode(self):
+        # not yet isolated to support multiple OW buses
+        return '''
+OneWire oneWire(%(pinNumber)s); 
+DallasTemperature sensors(&oneWire);
+DeviceAddress tempSensorAddress;
+#define NUM_TEMPERATURE_RETRIES 5
+
+void initSensors() {
+  sensors.begin();
+  sensors.getAddress(tempSensorAddress, 0);
+  sensors.setResolution(tempSensorAddress, 12);
+}
         
+        ''' % dict(pinNumber=self.pinNumber)
+    def generatePollCode(self):
+        return r'''
+for (int i=0; i<NUM_TEMPERATURE_RETRIES; i++) {
+  sensors.requestTemperatures();
+  float newTemp = sensors.getTempF(tempSensorAddress);
+  if (i < NUM_TEMPERATURE_RETRIES-1 && 
+      (newTemp < -100 || newTemp > 180)) {
+    // too many errors that were fixed by restarting arduino. 
+    // trying repeating this much init
+    initSensors();
+    continue;
+  }
+  Serial.print(newTemp);
+  Serial.print('\n');
+  Serial.print((char)i);
+  break;
+}
+        '''
+
+    def readFromPoll(self, read):
+        newTemp = readLine(read)
+        retries = ord(read(1))
+        return [
+            (self.uri, ROOM['temperatureF'], Literal(newTemp)),
+            (self.uri, ROOM['temperatureRetries'], Literal(retries)),
+            ]
+
+def makeDevices(graph, board):
+    out = []
+    for dt in sorted(_knownTypes, key=lambda cls: cls.__name__):
+        out.extend(dt.findInstances(graph, board))
+    return out
+