.NET Developer
Python Fanboy
Part Time Coffee Drinker
Linux Administrator
Cloud Architect
DIY Enthusiast
Open Source Developer
Cloud Architect
Automate All The Things Specialist
Core Python bot:
https://github.com/yanigisawa/coffee-scale
LED Svc:
https://github.com/akaczorek/ledsvc
Serverless DynamoDB:
https://github.com/chalfant/serverless-coffee-scale
Slides:
http://keirawong.com/blog/wp-content/uploads/2015/05/62110937.jpg?6bec58
uint16_t scales[NSCALES][2] = {\
// Stamps.com Model 510 5LB Scale
{0x1446, 0x6a73},
// USPS (Elane) PS311 "XM Elane Elane UParcel 30lb"
{0x7b7c, 0x0100},
// Stamps.com Stainless Steel 5 lb. Digital Scale
{0x2474, 0x0550},
// Stamps.com Stainless Steel 35 lb. Digital Scale
{0x2474, 0x3550},
// Mettler Toledo
{0x0eb8, 0xf000},
// SANFORD Dymo 10 lb USB Postal Scale
{0x6096, 0x0158},
// Fairbanks Scales SCB-R9000
{0x0b67, 0x555e},
// Dymo-CoStar Corp. M25 Digital Postal Scale
{0x0922, 0x8004},
// DYMO 1772057 Digital Postal Scale
{0x0922, 0x8003}
};
https://github.com/erjiang/usbscale/blob/master/scales.h
http://www.raywenderlich.com/wp-content/uploads/2014/01/pic2.png
dev = "/dev/usb/hiddev0"
fd = os.open(dev, os.O_RDONLY)
Integer Value | Name | File stream |
---|---|---|
0 | Standard Input | stdin |
1 | Standard Output | stdout |
2 | Standard Error | stderr |
with open("someFile.txt", "r") as f:
line = f.readline()
!=
fd is a File Descriptor
f is a File Object
from os import * # DON'T DO THIS
# Above import causes a name collision on "open"
with open("file.txt", "r") as f:
content = f.read()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: an integer is required
# Read 4 unsigned integers from USB device
hiddev_event_fmt = "IIII"
bytes_to_read = struct.calcsize(hiddev_event_fmt)
usb_binary_read = struct.unpack(hiddev_event_fmt, os.read(fd, bytes_to_read))
def getWeightInGrams(self, dev="/dev/usb/hiddev0"):
grams = -1
try:
fd = os.open(dev, os.O_RDONLY)
# Read 4 unsigned integers from USB device
hiddev_event_fmt = "IIII"
bytes_to_read = struct.calcsize(hiddev_event_fmt)
usb_binary_read = struct.unpack(hiddev_event_fmt, os.read(fd, bytes_to_read))
grams = usb_binary_read[3]
os.close(fd)
except OSError as e:
print("{0} - Failed to read from USB device".format(datetime.utcnow()))
return grams
def main(self):
self._currentWeight = self.getWeightInGrams()
while True:
try:
self._loopCount += 1
tmpWeight = self.getWeightInGrams()
if self.shouldLogWeight(tmpWeight):
self._currentWeight = tmpWeight
self.logToInitialState()
self.writeToDynamo()
if self.shouldPostToLed():
self._loopCount = 0
self.postToLed()
if self.potIsLifted():
self._mostRecentLiftedTime = datetime.now()
except Exception as e:
self._logger.error(e)
sleep(1)
def logToInitialState(self):
utcnow = datetime.utcnow()
bucketKey = "{0} - coffee_scale_data".format(self.environment)
streamer = Streamer(bucket_name="{0} - Coffee Scale Data".format(self.environment),
bucket_key=bucketKey, access_key=self.initialStateKey)
if self.potIsLifted():
streamer.log("Coffee Pot Lifted", True)
streamer.log("Coffee Weight", self._currentWeight)
streamer.close()
Combines AWS API Gateway with AWS Lambda (python, java, or nodejs)
Approaching 1.0
Hides complexity and implements deployment workflow
Makes simple APIs very easy and inexpensive
Rich error handling is still difficult
Framework is still a bit unstable as they head toward 1.0
Code with compiled libraries can be tricky to build/deploy
Enable API Gateway authentication!
https://github.com/chalfant/serverless-coffee-scale
Python code using boto3 library to read/write records to DynamoDb
Allowed for running code other than Arduino-Compatible
Multiple developers could easily contribute
Purchased ribbon cables for each end and patched the pinouts with breadboard wires
TESTIP=10.1.1.1
ping -c4 ${TESTIP} > /dev/null
if [ $? != 0 ]
then
logger -t $0 "WiFi seems down, restarting"
sudo /sbin/ifdown --force wlan0
sleep 10
sudo /sbin/ifup wlan0
fi
Run via cron every 5 minutes and call me in the morning
def render(mytext)
draw=Magick::Draw.new {
self.font_family = 'Comic Sans MS'
self.fill="#6495ED"
self.pointsize = 16
self.font_weight = 600
self.gravity = Magick::SouthWestGravity
}
metrics=draw.get_type_metrics(mytext)
image=Magick::Image.new(metrics['width']+30,16) {
self.background_color = "black"
self.format = "PPM"
self.depth = 8
}
draw.annotate(image,0,0,0,0,mytext) {
self.font_family = 'Comic Sans MS'
self.fill="#6495ED"
self.pointsize = 16
self.font_weight = 600
self.gravity = Magick::SouthWestGravity
}
Ruby ->
led-matrix -t 10 -D 1 runtext.ppm
C++ ->
class Display
extend Resque::Plugins::Logger
@queue = :leddisplay
def self.perform(localpath)
logger.info "displaying #{localpath}"
system("/root/ledsvc/led-matrix","-r16","-D1","-t30","#{localpath}")
File.delete(localpath)
rescue Resque::TermException
logger.error "display #{localpath} failed"
Resque.enqueue(self, localpath)
end
end
Ruby Resque to the Rescue: https://github.com/resque/resque
Process 'ledsvc'
status Running
monitoring status Monitored
pid 2247
parent pid 1
uptime 23d 0h 17m
children 0
memory kilobytes 32776
memory kilobytes total 32776
memory percent 7.3%
memory percent total 7.3%
cpu percent 1.6%
cpu percent total 1.6%
data collected Fri, 24 Jun 2016 13:23:31
I used monit: https://mmonit.com/monit/
Display can be daisy-chained. Let’s do that.
Variable time for message display
Fixed-display statistics plus scrolling messages