diff 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
line wrap: on
line diff
--- a/service/arduinoNode/devices.py	Sun Apr 12 03:43:20 2015 -0700
+++ b/service/arduinoNode/devices.py	Sun Apr 12 03:44:14 2015 -0700
@@ -1,6 +1,9 @@
+from __future__ import division
+import itertools
 from rdflib import Namespace, RDF, URIRef, Literal
 
 ROOM = Namespace('http://projects.bigasterisk.com/room/')
+XSD = Namespace('http://www.w3.org/2001/XMLSchema#')
 
 def readLine(read):
     buf = ''
@@ -18,7 +21,6 @@
         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;
@@ -28,8 +30,7 @@
                                initBindings=dict(board=board,
                                                  thisType=cls.deviceType),
                                initNs={'': ROOM}):
-            instances.append(cls(graph, row.dev, int(row.pinNumber)))
-        return instances
+            yield cls(graph, row.dev, int(row.pinNumber))
 
     # subclasses may add args to this
     def __init__(self, graph, uri, pinNumber):
@@ -63,11 +64,32 @@
 
     def generateActionCode(self):
         """
-        if you get called to do your action, this code reads the args you
-        need and do the right action
+        If the host side runs sendOutput, this C code will be run on the
+        board to receive whatever sendOutput writes. Each sendOutput
+        write(buf) call should be matched with len(buf) Serial.read()
+        calls in here.
         """
         return ''
        
+    def outputPatterns(self):
+        """
+        Triple patterns, using None as a wildcard, that should be routed
+        to sendOutput
+        """
+        return []
+
+    def sendOutput(self, statements, write, read):
+        """
+        If we got statements that match this class's outputPatterns, this
+        will be called with the statements that matched, and a serial
+        write method. What you write here will be available as
+        Serial.read in the generateActionCode C code.
+
+        Todo: it would be fine to read back confirmations or
+        whatever. Just need a way to collect them into graph statements.
+        """
+        raise NotImplementedError
+        
 _knownTypes = set()
 def register(deviceType):
     _knownTypes.add(deviceType)
@@ -91,12 +113,12 @@
     deviceType = ROOM['MotionSensor']
     def generateSetupCode(self):
         return 'pinMode(%(pin)d, INPUT); digitalWrite(%(pin)d, LOW);' % {
-            'pin': self.pinNumber(),
+            'pin': self.pinNumber,
         }
         
     def generatePollCode(self):
         return "Serial.write(digitalRead(%(pin)d) ? 'y' : 'n');" % {
-            'pin': self.pinNumber()
+            'pin': self.pinNumber
         }
         
     def readFromPoll(self, read):
@@ -123,15 +145,15 @@
 OneWire oneWire(%(pinNumber)s); 
 DallasTemperature sensors(&oneWire);
 DeviceAddress tempSensorAddress;
-#define NUM_TEMPERATURE_RETRIES 5
+#define NUM_TEMPERATURE_RETRIES 2
 
 void initSensors() {
   sensors.begin();
   sensors.getAddress(tempSensorAddress, 0);
   sensors.setResolution(tempSensorAddress, 12);
 }
+        ''' % dict(pinNumber=self.pinNumber)
         
-        ''' % dict(pinNumber=self.pinNumber)
     def generatePollCode(self):
         return r'''
 for (int i=0; i<NUM_TEMPERATURE_RETRIES; i++) {
@@ -155,10 +177,115 @@
         newTemp = readLine(read)
         retries = ord(read(1))
         return [
-            (self.uri, ROOM['temperatureF'], Literal(newTemp)),
+            (self.uri, ROOM['temperatureF'],
+             Literal(newTemp, datatype=XSD['decimal'])),
             (self.uri, ROOM['temperatureRetries'], Literal(retries)),
             ]
 
+def byteFromFloat(f):
+    return chr(int(min(255, max(0, f * 255))))
+        
+@register
+class LedOutput(DeviceType):
+    deviceType = ROOM['LedOutput']
+    def generateSetupCode(self):
+        return 'pinMode(%(pin)d, OUTPUT); digitalWrite(%(pin)d, LOW);' % {
+            'pin': self.pinNumber,
+        }
+ 
+    def outputPatterns(self):
+        return [(self.uri, ROOM['brightness'], None)]
+
+    def sendOutput(self, statements, write, read):
+        assert len(statements) == 1
+        assert statements[0][:2] == (self.uri, ROOM['brightness'])
+        value = float(statements[0][2])
+        if (self.uri, RDF.type, ROOM['ActiveLowOutput']):
+            value = 1 - value
+        write(byteFromFloat(value))
+        
+    def generateActionCode(self):
+        return r'''
+          while(Serial.available() < 1) NULL;
+          analogWrite(%(pin)d, Serial.read());
+        ''' % dict(pin=self.pinNumber)
+        
+@register
+class ST7576Lcd(DeviceType):
+    deviceType = ROOM['ST7565Lcd']
+    @classmethod
+    def findInstances(cls, graph, board):
+        grouped = itertools.groupby(
+            graph.query("""SELECT DISTINCT ?dev ?pred ?pinNumber WHERE {
+                                    ?board :hasPin ?pin .
+                                    ?pin :pinNumber ?pinNumber;
+                                         :connectedTo ?devPin .
+                                    ?dev a :ST7565Lcd .
+                                    ?dev ?pred ?devPin .
+                                  } ORDER BY ?dev""",
+                               initBindings=dict(board=board,
+                                                 thisType=cls.deviceType),
+                               initNs={'': ROOM}),
+            lambda row: row.dev)
+        for dev, connections in grouped:
+            connections = dict((role, int(num)) for unused_dev, role, num
+                               in connections)
+            yield cls(graph, dev, connections=connections)
+
+    def __init__(self, graph, dev, connections):
+        super(ST7576Lcd, self).__init__(graph, dev, pinNumber=None)
+        self.connections = connections
+
+    def generateIncludes(self):
+        return ['ST7565.h']
+
+    def generateArduinoLibs(self):
+        return ['ST7565']
+        
+    def generateGlobalCode(self):
+        return '''
+          ST7565 glcd(%(SID)d, %(SCLK)d, %(A0)d, %(RST)d, %(CS)d);
+          char newtxt[21*8+1];
+          unsigned int written;
+        ''' % dict(SID=self.connections[ROOM['lcdSID']],
+                   SCLK=self.connections[ROOM['lcdSCLK']],
+                   A0=self.connections[ROOM['lcdA0']],
+                   RST=self.connections[ROOM['lcdRST']],
+                   CS=self.connections[ROOM['lcdCS']])
+                   
+    def generateSetupCode(self):
+        return '''
+          glcd.st7565_init();
+          glcd.st7565_command(CMD_DISPLAY_ON);
+          glcd.st7565_command(CMD_SET_ALLPTS_NORMAL);
+          glcd.st7565_set_brightness(0x18);
+        
+          glcd.display(); // show splashscreen
+        '''
+
+    def outputPatterns(self):
+        return [(self.uri, ROOM['text'], None)]
+
+    def sendOutput(self, statements, write, read):
+        assert len(statements) == 1
+        assert statements[0][:2] == (self.uri, ROOM['text'])
+        value = str(statements[0][2])
+        assert len(value) < 254, repr(value)
+        write(chr(len(value)) + value)
+
+    def generateActionCode(self):
+        return '''
+          while(Serial.available() < 1) NULL;
+          byte bufSize = Serial.read();
+          for (byte i = 0; i < bufSize; i++) {
+            while(Serial.available() < 1) NULL;
+            newtxt[i] = Serial.read();
+          }
+          glcd.clear();
+          glcd.drawstring(0,0, newtxt); 
+          glcd.display();
+        '''
+
 def makeDevices(graph, board):
     out = []
     for dt in sorted(_knownTypes, key=lambda cls: cls.__name__):