changeset 34:90ff65ccd46b

garage: videoselect and shiftbrite move from parallel to arduino Ignore-this: 45c791caa87d4c054826020eeae8298b
author drewp@bigasterisk.com
date Mon, 25 Jun 2012 00:50:33 -0700
parents c3c203100e06
children 912a3dae5c2c
files service/garageArduino/garage/garage.pde service/garageArduino/garageArduino.py service/garageArduino/index.html
diffstat 3 files changed, 155 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/service/garageArduino/garage/garage.pde	Mon May 07 00:17:40 2012 -0700
+++ b/service/garageArduino/garage/garage.pde	Mon Jun 25 00:50:33 2012 -0700
@@ -1,4 +1,51 @@
- void setup()   {                
+/*
+board is Arduino UNO with '328
+ */
+
+int datapin  = 7; // DI
+int latchpin = 9; // LI
+int clockpin = 8; // CI
+
+unsigned long SB_CommandPacket;
+int SB_CommandMode;
+int SB_BlueCommand;
+int SB_RedCommand;
+int SB_GreenCommand;
+
+#define SHIFT(val) shiftOut(datapin, clockpin, MSBFIRST, val)
+
+void SB_SendPacket() {
+  /* high bits are 00 for color, 01 for current */
+   SB_CommandPacket = SB_CommandMode & B11;
+   SB_CommandPacket = (SB_CommandPacket << 10)  | (SB_BlueCommand & 1023);
+   SB_CommandPacket = (SB_CommandPacket << 10)  | (SB_RedCommand & 1023);
+   SB_CommandPacket = (SB_CommandPacket << 10)  | (SB_GreenCommand & 1023);
+
+   SHIFT(SB_CommandPacket >> 24);
+   SHIFT(SB_CommandPacket >> 16);
+   SHIFT(SB_CommandPacket >> 8);
+   SHIFT(SB_CommandPacket);
+}
+
+void latch() {
+   delayMicroseconds(100);
+   digitalWrite(latchpin,HIGH); // latch data into registers
+   delayMicroseconds(100);
+   digitalWrite(latchpin,LOW); 
+}
+
+void setCurrent(byte r, byte g, byte b) { 
+ /* 127 = max */ 
+   SB_CommandMode = B01; // Write to current control registers
+   SB_RedCommand = r; 
+   SB_GreenCommand = g;
+   SB_BlueCommand = b;
+   SB_SendPacket();
+   latch();
+}
+
+
+void setup()   {                
   
   pinMode(2, INPUT);
   digitalWrite(2, LOW); 
@@ -16,6 +63,21 @@
   // circuitry to prevent such glitches, so a pull-down
   // resistor may be enough. I haven't checked carefully.
 
+  pinMode(4, OUTPUT);
+  pinMode(5, OUTPUT);
+  // video input selector. picks which video input will be multiplexed
+  // into the bt848 capture card
+
+  pinMode(6, OUTPUT);  // front yard light
+
+  pinMode(7, OUTPUT); // bathroom shiftbrite data
+  pinMode(8, OUTPUT); // bathroom shiftbrite clk
+  pinMode(9, OUTPUT); // bathroom shiftbrite latch
+
+  for (int i=0; i < 1; i++) {
+    setCurrent(127, 127, 127);
+  }
+
   Serial.begin(115200);
 }
 
@@ -74,7 +136,30 @@
       Serial.print("{\"garage\":");
       Serial.print(arg ? "true" : "false");
       Serial.print("}\n");     
-    }
+    } else if (cmd == 0x05) { // set video	
+      digitalWrite(4, arg & 1);
+      digitalWrite(5, arg & 2);
+      Serial.print("{\"videoSelect\":");
+      Serial.print(arg);
+      Serial.print("}\n");     
+    } else if (cmd == 0x06) { // talk to shiftbrite
+      /*
+        one byte for the string length, then a buffer to be shifted
+        out to all the shiftbrites
+      */
+      /*
+      for (int i=0; i < arg / 4; i++) {
+        setCurrent(127, 127, 127);
+      }
+      */
+      for (int i=0; i<arg; i++) {
+        while (Serial.available() == 0) NULL;
+        SHIFT(Serial.read());
+      }
+      latch();
+      Serial.print("{\"ok\":1}\n");
+
+    }   
   }
 }
 
--- a/service/garageArduino/garageArduino.py	Mon May 07 00:17:40 2012 -0700
+++ b/service/garageArduino/garageArduino.py	Mon Jun 25 00:50:33 2012 -0700
@@ -5,7 +5,7 @@
 
 from __future__ import division
 
-import cyclone.web, json, traceback, os, sys, time, logging
+import cyclone.web, json, traceback, os, sys, time, logging, bitstring
 from twisted.internet import reactor, task, defer
 from twisted.web.client import getPage
 sys.path.append("/my/proj/house/frontdoor")
@@ -18,6 +18,15 @@
 sys.path.append("/my/site/magma")
 from stategraph import StateGraph      
 from rdflib import Namespace, RDF, Literal
+from webcolors import hex_to_rgb, rgb_to_hex
+
+def rgbFromHex(h):
+    """returns tuple of 0..1023"""
+    norm = hex_to_rgb(h)
+    return tuple([x * 4 for x in norm])
+
+def hexFromRgb(rgb):
+    return rgb_to_hex(tuple([x // 4 for x in rgb]))
 
 
 ROOM = Namespace("http://projects.bigasterisk.com/room/")
@@ -52,6 +61,25 @@
         self.ser.write("\x60\x04"+chr(int(bool(level))))
         return self.ser.readJson()['garage']
 
+    def setVideoSelect(self, chan):
+        """set video select bits from 0..3"""
+        self.ser.write("\x60\x05"+chr(chan))
+        return self.ser.readJson()['videoSelect']
+
+    def shiftbrite(self, colors):
+        """
+        shift out this sequence of (r,g,b) triples of 10-bit ints
+        """
+        current = "".join(bitstring.pack("0b01, uint:10, uint:10, uint:10",
+                                         127, 127, 127).bytes
+                          for loop in range(len(colors)))
+        out = "".join(bitstring.pack("0b00, uint:10, uint:10, uint:10",
+                                     b, r, g).bytes
+                      for r,g,b in colors)
+        out = current + out
+        self.ser.write("\x60\x06" + chr(len(out)) + out)
+        msg = self.ser.readJson()
+        assert msg == {"ok":1}, msg
 
 class Index(PrettyErrorHandler, cyclone.web.RequestHandler):
     def get(self):
@@ -132,6 +160,23 @@
         reactor.callLater(1.5, finish) # this time depends on the LP circuit
         return d
 
+class VideoSelect(PrettyErrorHandler, cyclone.web.RequestHandler):
+    def post(self): 
+        self.set_header("Content-Type", "application/javascript")
+        v = self.settings.arduino.setVideoSelect(int(self.request.body))
+        self.write(json.dumps({"videoSelect" : v}))
+
+class Brite(PrettyErrorHandler, cyclone.web.RequestHandler):
+    def get(self, chan):
+        self.set_header("Content-Type", "text/plain")
+        self.write(hexFromRgb(self.settings.colors[int(chan)]))
+        
+    def put(self, chan):
+        s = self.settings
+        s.colors[int(chan)] = rgbFromHex(self.request.body)
+        s.arduino.shiftbrite(s.colors)
+    post = put
+
 class Application(cyclone.web.Application):
     def __init__(self, ard, poller):
         handlers = [
@@ -142,11 +187,13 @@
             (r'/housePower/raw', HousePowerRaw),
             (r'/housePower/threshold', HousePowerThreshold),
             (r'/garageDoorOpen', GarageDoorOpen),
+            (r'/videoSelect', VideoSelect), 
+            (r"/brite/(\d+)", Brite),
         ]
-        settings = {"arduino" : ard, "poller" : poller}
+        colors = [(0,0,0)] * 1 # stored 10-bit
+        settings = {"arduino" : ard, "poller" : poller, "colors" : colors}
         cyclone.web.Application.__init__(self, handlers, **settings)
 
-
 class Poller(object):
     """
     times the blinks to estimate power usage. Captures the other
--- a/service/garageArduino/index.html	Mon May 07 00:17:40 2012 -0700
+++ b/service/garageArduino/index.html	Mon Jun 25 00:50:33 2012 -0700
@@ -29,8 +29,12 @@
 
     <p><button type="submit" id="refresh">refresh</button></p>
 
+    <p>Bathroom shiftbrite</p>
+    <div class="colorpicker" id="p0"></div>
+
     <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"/>
-
+    <script type="text/javascript" src="http://bigasterisk.com/bathroomLight/static/farbtastic/farbtastic.js"></script>
+    <link rel="stylesheet" href="http://bigasterisk.com/bathroomLight/static/farbtastic/farbtastic.css" type="text/css" />
 
     <script type="text/javascript">
      // <![CDATA[
@@ -71,6 +75,19 @@
 	}
 	refresh();
 	$("#refresh").click(refresh);
+
+        $.each([
+            ["#p0", "brite/0"],
+        ], function (i, row) {
+            var picker = $.farbtastic(row[0], function (newColor) {
+                $.ajax({
+                    type: "put", url: row[1],
+                    contentType: "text/plain", data: newColor
+                });
+            });
+            $.get(row[1], picker.setColor);
+        });
+
     });
      // ]]>
     </script>