899
|
1 package main
|
|
2
|
|
3 import (
|
|
4 "io/ioutil"
|
|
5 "log"
|
|
6 "net/http"
|
|
7 "strconv"
|
901
|
8 "time"
|
899
|
9 "encoding/json"
|
|
10 "github.com/bmizerany/pat"
|
|
11 "github.com/mrmorphic/hwio"
|
|
12 )
|
|
13
|
|
14 /*
|
|
15 hwio.DebugPinMap() wrote this:
|
|
16
|
|
17 Pin 1: 3.3V, cap:
|
|
18 Pin 2: 5V, cap:
|
|
19 Pin 3: SDA,GPIO0 cap:output,input,input_pullup,input_pulldown
|
|
20 Pin 5: SCL,GPIO1 cap:output,input,input_pullup,input_pulldown
|
|
21 Pin 6: GROUND, cap:
|
|
22 Pin 7: GPIO4 cap:output,input,input_pullup,input_pulldown
|
|
23 Pin 8: TXD,GPIO14 cap:output,input,input_pullup,input_pulldown
|
|
24 Pin 10: RXD,GPIO15 cap:output,input,input_pullup,input_pulldown
|
|
25 Pin 11: GPIO17 cap:output,input,input_pullup,input_pulldown
|
|
26 Pin 12: GPIO18 cap:output,input,input_pullup,input_pulldown
|
|
27 Pin 13: GPIO21 cap:output,input,input_pullup,input_pulldown
|
|
28 Pin 15: GPIO22 cap:output,input,input_pullup,input_pulldown
|
|
29 Pin 16: GPIO23 cap:output,input,input_pullup,input_pulldown
|
|
30 Pin 18: GPIO24 cap:output,input,input_pullup,input_pulldown
|
|
31 Pin 19: MOSI,GPIO10 cap:output,input,input_pullup,input_pulldown
|
|
32 Pin 21: MISO,GPIO9 cap:output,input,input_pullup,input_pulldown
|
|
33 Pin 22: GPIO25 cap:output,input,input_pullup,input_pulldown
|
|
34 Pin 23: SCLK,GPIO11 cap:output,input,input_pullup,input_pulldown
|
|
35 Pin 24: CE0N,GPIO8 cap:output,input,input_pullup,input_pulldown
|
|
36 Pin 26: CE1N,GPIO7 cap:output,input,input_pullup,input_pulldown
|
|
37 */
|
|
38
|
|
39 type Pins struct {
|
|
40 InMotion, InSwitch3, InSwitch1, InSwitch2, OutLed, OutSpeaker, InDoorClosed, OutStrike hwio.Pin
|
|
41 LastOutLed, LastOutStrike int
|
|
42 }
|
|
43
|
|
44 // hwio.GetPin with a panic instead of an error return
|
|
45 func GetPin(id string) hwio.Pin {
|
|
46 p, e := hwio.GetPin(id)
|
|
47 if e != nil {
|
|
48 panic(e)
|
|
49 }
|
|
50 return p
|
|
51 }
|
|
52
|
|
53 func DigitalRead(p hwio.Pin) int {
|
|
54 v, err := hwio.DigitalRead(p)
|
|
55 if err != nil {
|
|
56 panic(err)
|
|
57 }
|
|
58 return v
|
|
59 }
|
|
60
|
|
61 func SetupIo() Pins {
|
|
62 pins := Pins{
|
|
63 InMotion: GetPin("GPIO0"),
|
|
64 InSwitch3: GetPin("GPIO1"),
|
|
65 InSwitch1: GetPin("GPIO4"),
|
|
66 InSwitch2: GetPin("GPIO17"),
|
|
67 OutLed: GetPin("GPIO21"),
|
|
68 OutSpeaker: GetPin("GPIO22"),
|
|
69 InDoorClosed: GetPin("GPIO10"),
|
|
70 OutStrike: GetPin("GPIO9"),
|
|
71 }
|
|
72
|
903
|
73 if err := hwio.PinMode(pins.InMotion, hwio.INPUT_PULLUP); err != nil { panic(err) }
|
|
74 if err := hwio.PinMode(pins.InSwitch1, hwio.INPUT_PULLUP); err != nil { panic(err) }
|
|
75 if err := hwio.PinMode(pins.InSwitch2, hwio.INPUT_PULLUP); err != nil { panic(err) }
|
|
76 if err := hwio.PinMode(pins.InSwitch3, hwio.INPUT_PULLUP); err != nil { panic(err) }
|
|
77 if err := hwio.PinMode(pins.InDoorClosed, hwio.INPUT_PULLUP); err != nil { panic(err) }
|
|
78 if err := hwio.PinMode(pins.OutLed, hwio.OUTPUT); err != nil { panic(err) }
|
|
79 if err := hwio.PinMode(pins.OutSpeaker, hwio.OUTPUT); err != nil { panic(err) }
|
|
80 if err := hwio.PinMode(pins.OutStrike, hwio.OUTPUT); err != nil { panic(err) }
|
899
|
81 return pins
|
|
82 }
|
|
83
|
901
|
84
|
|
85 func booleanBody(w http.ResponseWriter, r *http.Request) (level int, err error) {
|
|
86 body, err := ioutil.ReadAll(r.Body)
|
|
87 if err != nil {
|
|
88 panic(err)
|
|
89 }
|
|
90 level, err2 := strconv.Atoi(string(body[:]))
|
|
91 if err2 != nil {
|
|
92 http.Error(w, "body must be '0' or '1'", http.StatusBadRequest)
|
|
93 return 0, err
|
|
94 }
|
|
95 return level, nil
|
|
96 }
|
|
97
|
899
|
98 func main() {
|
|
99 pins := SetupIo()
|
|
100
|
|
101 m := pat.New()
|
|
102
|
|
103 m.Get("/", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
|
901
|
104 // this one needs to fail if the hardware is broken in
|
|
105 // any way that we can determine, though I'm not sure
|
|
106 // what that will mean on rpi
|
899
|
107 http.ServeFile(w, r, "index.html")
|
|
108 }));
|
|
109
|
|
110 m.Get("/static/:any", http.FileServer(http.Dir("./")));
|
|
111
|
|
112 m.Get("/status", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
|
|
113 jsonEncode := json.NewEncoder(w)
|
|
114 jsonEncode.Encode(map[string]int{
|
|
115 "motion": DigitalRead(pins.InMotion),
|
|
116 "switch1": DigitalRead(pins.InSwitch1),
|
|
117 "switch2": DigitalRead(pins.InSwitch2),
|
|
118 "switch3": DigitalRead(pins.InSwitch3),
|
|
119 "doorClosed": DigitalRead(pins.InDoorClosed),
|
|
120 "led": pins.LastOutLed,
|
901
|
121 "strike": pins.LastOutStrike,
|
899
|
122 })
|
|
123 }));
|
901
|
124
|
899
|
125 m.Put("/led", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
|
|
126 body, err := ioutil.ReadAll(r.Body)
|
|
127 if err != nil {
|
|
128 panic(err)
|
|
129 }
|
901
|
130 var level int
|
|
131 if string(body) == "on" {
|
|
132 level = 1
|
|
133 } else if string(body) == "off" {
|
|
134 level = 0
|
|
135 } else {
|
|
136 http.Error(w, "body must be 'on' or 'off'", http.StatusBadRequest)
|
|
137 return
|
899
|
138 }
|
|
139
|
|
140 hwio.DigitalWrite(pins.OutLed, level)
|
|
141 pins.LastOutLed = level
|
|
142 http.Error(w, "", http.StatusAccepted)
|
901
|
143 }))
|
899
|
144
|
901
|
145 setStrike := func (level int) {
|
|
146 hwio.DigitalWrite(pins.OutStrike, level)
|
|
147 pins.LastOutStrike = level
|
|
148 }
|
|
149
|
|
150 m.Put("/strike", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
|
|
151 level, err := booleanBody(w, r)
|
|
152 if err != nil {
|
|
153 panic(err)
|
|
154 }
|
|
155 setStrike(level)
|
|
156 http.Error(w, "", http.StatusAccepted)
|
|
157 }))
|
|
158
|
|
159 m.Put("/strike/temporaryUnlock", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
|
|
160 err := r.ParseForm()
|
|
161 if err != nil {
|
|
162 panic(err)
|
|
163 }
|
|
164 seconds, err2 := strconv.ParseFloat(string(r.Form["seconds"][0]), 32)
|
|
165 if err2 != nil {
|
|
166 http.Error(w, "seconds must be a float", http.StatusBadRequest)
|
|
167 return
|
|
168 }
|
|
169
|
|
170 // This is not correctly reentrant. There should be a
|
|
171 // stack of temporary effects that unpop correctly,
|
|
172 // and status should show you any running effects.
|
|
173 setStrike(1)
|
|
174 go func() {
|
|
175 time.Sleep(time.Duration(seconds * float64(time.Second)))
|
|
176 setStrike(0)
|
|
177 }()
|
|
178 http.Error(w, "", http.StatusAccepted)
|
|
179 }))
|
|
180
|
|
181 m.Put("/speaker/beep", http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
|
|
182 // queue a beep
|
|
183 http.Error(w, "", http.StatusAccepted)
|
|
184 }))
|
|
185
|
899
|
186 http.Handle("/", m)
|
901
|
187 log.Printf("Listening on port 8081")
|
|
188 err := http.ListenAndServe(":8081", nil)
|
899
|
189 if err != nil {
|
|
190 log.Fatal("ListenAndServe: ", err)
|
|
191 }
|
|
192 }
|