my haskell controlled offgrid fridge

综合技术 2018-04-22 阅读原文

I'm preparing for a fridge upgrade, away from the tiny propane fridge to a chest freezer conversion. My home computer will be monitoring the fridge temperature and the state of my offgrid energy system, and turning the fridge on and off using a relay and the inverter control board I built earlier.

This kind of automation is a perfect fit for Functional Reactive Programming (FRP) since it's all about time-varying behaviors and events being combined together.

Of course, I want the control code to be as robust as possible, well tested, and easy to modify without making mistakes. Pure functional Haskell code.

There are many Haskell libraries for FRP, and I have not looked at most of them in any detail. I settled on reactive-banana because it has a good reputation and amazing testimonials.

"In the programming-language world, one rule of survival is simple: dance or die. This library makes dancing easy." – Simon Banana Jones

But, it's mostly used for GUI programming, or maybe some musical live-coding. There were no libraries for using reactive-banana for the more staid task of home automation, or anything like that. Also, using it involves a whole lot of IO code, so not great for testing.

So I built reactive-banana-automation on top of it to address my needs. I think it's a pretty good library, although I don't have a deep enough grokking of FRP to say that for sure.

Anyway, it's plenty flexible for my fridge automation needs, and I also wrote a motion-controlled light automation with it to make sure it could be used for something else (and to partly tackle the problem of using real-world time events when the underlying FRP library uses its own notion of time).

The code for my fridge is a work in progress since the fridge has not arrived yet, and because the question of in which situations an offgrid fridge should optimally run and not run is really rather complicated.

Here's a simpler example, for a non-offgrid fridge.

fridge :: Automation Sensors Actuators
fridge sensors actuators = do
        -- Create a Behavior that reflects the most recently reported
        -- temperature of the fridge.
        btemperature <- sensedBehavior (fridgeTemperature sensors)
        -- Calculate when the fridge should turn on and off.
        let bpowerchange = calcpowerchange  btemperature
        onBehaviorChangeMaybe bpowerchange (actuators . FridgePower)
  where
        calcpowerchange (Sensed temp)
                | temp `belowRange` allowedtemp = Just PowerOff
                | temp `aboveRange` allowedtemp = Just PowerOn
                | otherwise = Nothing
        calcpowerchange SensorUnavailable = Nothing
        allowedtemp = Range 1 4

And here the code is being tested in a reproducible fashion:

> runner  runner $ sensors -> fridgeTemperature sensors =: 6
[FridgePower PowerOn]
> runner $ sensors -> fridgeTemperature sensors =: 3
[]
> runner $ sensors -> fridgeTemperature sensors =: 0.5
[FridgePower PowerOff]

BTW, building a 400 line library and writing reams of control code for a fridge that has not been installed yet is what we Haskell programmers call "laziness".

责编内容by:joeyh 【阅读原文】。感谢您的支持!

您可能感兴趣的

Haskell简明教程(一):从递归说起 这一系列是我学习 Learn You a Haskell For Great Good 之后,总结,编写的学习笔记。 这个系列主要分为五个部分: 从命令式语言进行抽象 ...
Coding Haskell and F# with Daniel Chambers Carl Franklin is Executive Vice President of App vNext , a software development firm focused on the latest...
基于 Generator 和 Iterator 的惰性列表 初识 Lazy List 如果有了解过 Haskell 的朋友,对下面的这些表达一定不陌生 repeat 1 -- => cycle "abc" -- => "abcabcabc..." -- => ...
Using types to unit-test in Haskell Object-oriented programming languages make unit testing easy by providing obvious boundaries between units of code in th...
Deprecating the Haskell markdown library Back in 2012, I was working on a closed-source project which required a Markdown parsing library. At the time, the o...