diff service/arduinoNode/devices.py @ 975:f3023410d875

polymer board debug page with working output widgets Ignore-this: 3157d0c47a91afe47b30a5f182629d93 darcs-hash:20150414063012-312f9-69ac15a9c7bab2b3b5ca00142fe4435ded7d6d0f
author drewp <drewp@bigasterisk.com>
date Mon, 13 Apr 2015 23:30:12 -0700
parents f707210c13bd
children 715c1c42185e
line wrap: on
line diff
--- a/service/arduinoNode/devices.py	Sun Apr 12 03:44:14 2015 -0700
+++ b/service/arduinoNode/devices.py	Mon Apr 13 23:30:12 2015 -0700
@@ -36,6 +36,16 @@
     def __init__(self, graph, uri, pinNumber):
         self.graph, self.uri = graph, uri
         self.pinNumber = pinNumber
+
+    def description(self):
+        return {
+            'uri': self.uri,
+            'className': self.__class__.__name__,
+            'pinNumber': self.pinNumber,
+            'outputPatterns': self.outputPatterns(),
+            'watchPrefixes': self.watchPrefixes(),
+            'outputWidgets': self.outputWidgets(),
+        }
         
     def readFromPoll(self, read):
         """
@@ -46,20 +56,37 @@
         """
         raise NotImplementedError('readFromPoll in %s' % self.__class__)
 
+    def watchPrefixes(self):
+        """
+        subj,pred pairs of the statements that might be returned from
+        readFromPoll, so the dashboard knows what it should
+        watch. This should be eliminated, as the dashboard should just
+        always watch the whole tree of statements starting self.uri
+        """
+        return []
+        
     def generateIncludes(self):
+        """filenames of .h files to #include"""
         return []
 
     def generateArduinoLibs(self):
+        """names of libraries for the ARDUINO_LIBS line in the makefile"""
         return []
         
     def generateGlobalCode(self):
+        """C code to emit in the global section"""
         return ''
         
     def generateSetupCode(self):
+        """C code to emit in setup()"""
         return ''
         
     def generatePollCode(self):
-        """if this returns nothing, we don't try to poll this device"""
+        """
+        C code to run a poll update. This should Serial.write its output
+        for readFromPoll to consume. If this returns nothing, we don't
+        try to poll this device.
+        """
         return ''
 
     def generateActionCode(self):
@@ -78,6 +105,13 @@
         """
         return []
 
+    def outputWidgets(self):
+        """
+        structs to make output widgets on the dashboard. ~1 of these per
+        handler you have in sendOutput
+        """
+        return []
+        
     def sendOutput(self, statements, write, read):
         """
         If we got statements that match this class's outputPatterns, this
@@ -103,11 +137,15 @@
     
     def generatePollCode(self):
         return "Serial.write('k');"
+        
     def readFromPoll(self, read):
         if read(1) != 'k':
             raise ValueError('invalid ping response')
         return [(self.uri, ROOM['ping'], ROOM['ok'])]
 
+    def watchPrefixes(self):
+        return [(self.uri, ROOM['ping'])]
+
 @register
 class MotionSensorInput(DeviceType):
     deviceType = ROOM['MotionSensor']
@@ -129,8 +167,15 @@
         return [(self.uri, ROOM['sees'],
                  ROOM['motion'] if motion else ROOM['noMotion'])]
 
+    def watchPrefixes(self):
+        return [(self.uri, ROOM['sees'])]
+
 @register
 class OneWire(DeviceType):
+    """
+    A OW bus with temperature sensors (and maybe other devices, which
+    are also to be handled under this object)
+    """
     deviceType = ROOM['OneWire']
    
     def generateIncludes(self):
@@ -176,12 +221,20 @@
     def readFromPoll(self, read):
         newTemp = readLine(read)
         retries = ord(read(1))
+        # uri will change; there could (likely) be multiple connected sensors
         return [
             (self.uri, ROOM['temperatureF'],
              Literal(newTemp, datatype=XSD['decimal'])),
             (self.uri, ROOM['temperatureRetries'], Literal(retries)),
             ]
 
+    def watchPrefixes(self):
+        # these uris will become dynamic! see note on watchPrefixes
+        # about eliminating it.
+        return [(self.uri, ROOM['temperatureF']),
+                (self.uri, ROOM['temperatureRetries']),
+                ]
+
 def byteFromFloat(f):
     return chr(int(min(255, max(0, f * 255))))
         
@@ -209,6 +262,46 @@
           while(Serial.available() < 1) NULL;
           analogWrite(%(pin)d, Serial.read());
         ''' % dict(pin=self.pinNumber)
+
+    def outputWidgets(self):
+        return [{
+            'element': 'output-slider',
+            'min': 0,
+            'max': 1,
+            'step': 1 / 255,
+            'subj': self.uri,
+            'pred': ROOM['brightness'],
+        }]
+
+@register
+class DigitalOutput(DeviceType):
+    deviceType = ROOM['DigitalOutput']
+    def generateSetupCode(self):
+        return 'pinMode(%(pin)d, OUTPUT); digitalWrite(%(pin)d, LOW);' % {
+            'pin': self.pinNumber,
+        }
+ 
+    def outputPatterns(self):
+        return [(self.uri, ROOM['level'], None)]
+
+    def sendOutput(self, statements, write, read):
+        assert len(statements) == 1
+        assert statements[0][:2] == (self.uri, ROOM['level'])
+        value = {"high": 1, "low": 0}[str(statements[0][2])]
+        write(chr(value))
+        
+    def generateActionCode(self):
+        return r'''
+          while(Serial.available() < 1) NULL;
+          digitalWrite(%(pin)d, Serial.read());
+        ''' % dict(pin=self.pinNumber)
+        
+    def outputWidgets(self):
+        return [{
+            'element': 'output-switch',
+            'subj': self.uri,
+            'pred': ROOM['level'],
+        }]
         
 @register
 class ST7576Lcd(DeviceType):
@@ -273,14 +366,26 @@
         assert len(value) < 254, repr(value)
         write(chr(len(value)) + value)
 
+    def outputWidgets(self):
+        return [{
+                'element': 'output-fixed-text',
+                'cols': 21,
+                'rows': 8,
+                'subj': self.uri,
+                'pred': ROOM['text'],
+            }]
+        
     def generateActionCode(self):
         return '''
           while(Serial.available() < 1) NULL;
           byte bufSize = Serial.read();
-          for (byte i = 0; i < bufSize; i++) {
+          for (byte i = 0; i < bufSize; ++i) {
             while(Serial.available() < 1) NULL;
             newtxt[i] = Serial.read();
           }
+          for (byte i = bufSize; i < sizeof(newtxt); ++i) {
+            newtxt[i] = 0;
+          }
           glcd.clear();
           glcd.drawstring(0,0, newtxt); 
           glcd.display();