--- /dev/null
+.fake/
+.paket/
+paket.lock
+packages/
+*.so
+obj
+bin
+build/
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>\r
+<configuration>\r
+ <startup> \r
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />\r
+ </startup>\r
+</configuration>
\ No newline at end of file
--- /dev/null
+namespace PiNet.AssemblyInfo\r
+\r
+open System.Reflection\r
+open System.Runtime.CompilerServices\r
+open System.Runtime.InteropServices\r
+\r
+// General Information about an assembly is controlled through the following \r
+// set of attributes. Change these attribute values to modify the information\r
+// associated with an assembly.\r
+[<assembly: AssemblyTitle("PiNet")>]\r
+[<assembly: AssemblyDescription("")>]\r
+[<assembly: AssemblyConfiguration("")>]\r
+[<assembly: AssemblyCompany("")>]\r
+[<assembly: AssemblyProduct("PiNet")>]\r
+[<assembly: AssemblyCopyright("Copyright © 2016")>]\r
+[<assembly: AssemblyTrademark("")>]\r
+[<assembly: AssemblyCulture("")>]\r
+\r
+// Setting ComVisible to false makes the types in this assembly not visible \r
+// to COM components. If you need to access a type in this assembly from \r
+// COM, set the ComVisible attribute to true on that type.\r
+[<assembly: ComVisible(false)>]\r
+\r
+// The following GUID is for the ID of the typelib if this project is exposed to COM\r
+[<assembly: Guid("f2329c6d-a6a4-48fb-a595-673b9f892e8e")>]\r
+\r
+// Version information for an assembly consists of the following four values:\r
+// \r
+// Major Version\r
+// Minor Version \r
+// Build Number\r
+// Revision\r
+// \r
+// You can specify all the values or you can default the Build and Revision Numbers \r
+// by using the '*' as shown below:\r
+// [<assembly: AssemblyVersion("1.0.*")>]\r
+[<assembly: AssemblyVersion("1.0.0.0")>]\r
+[<assembly: AssemblyFileVersion("1.0.0.0")>]\r
+\r
+do\r
+ ()
\ No newline at end of file
--- /dev/null
+module EtoUtils
+
+open System
+open Eto
+open Eto.Forms
+open Eto.Drawing
+
+type TCell =
+| El of Control
+| StretchedEl of Control
+| EmptyElement
+| TableEl of Table
+
+and TRow =
+| Row of TCell list
+| StretchedRow of TCell list
+| Spacing of Size
+| Pad of Padding
+
+and Table = Tbl of TRow list
+
+let rec makeLayout (Tbl t) =
+ let ret = new TableLayout()
+ for r in t do
+ // Build one row of the table.
+ let makeTd (tds: TCell list) =
+ let row = TableRow()
+ for td in tds do
+ match td with
+ | El c -> row.Cells.Add(TableCell(c, false))
+ | StretchedEl c -> row.Cells.Add(TableCell(c, true))
+ | EmptyElement -> row.Cells.Add(TableCell(null, true))
+ | TableEl t -> row.Cells.Add(TableCell(makeLayout t, true))
+ row
+ match r with
+ | Row tds -> let r = makeTd tds in ret.Rows.Add(r)
+ | StretchedRow tds -> let r = makeTd tds in r.ScaleHeight <- true; ret.Rows.Add(r)
+ | Spacing sz -> ret.Spacing <- sz
+ | Pad pad -> ret.Padding <- pad
+ ret
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Name>PiNet</Name>
+ <AssemblyName>PiNet</AssemblyName>
+ <RootNamespace>PiNet</RootNamespace>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>274c6bcd-9088-463a-8725-d23b7f60de89</ProjectGuid>
+ <ProjectType />
+ <OutputType>Exe</OutputType>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <TargetFrameworkProfile />
+ <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+ <TargetFSharpCoreVersion>4.3.1.0</TargetFSharpCoreVersion>
+ <DocumentationFile />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>Full</DebugType>
+ <Optimize>false</Optimize>
+ <Tailcalls>false</Tailcalls>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUGTRACE</DefineConstants>
+ <WarningLevel>3</WarningLevel>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <Prefer32Bit>true</Prefer32Bit>
+ <OtherFlags />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugSymbols />
+ <DebugType>PdbOnly</DebugType>
+ <Optimize>true</Optimize>
+ <Tailcalls>true</Tailcalls>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <WarningLevel>3</WarningLevel>
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <Prefer32Bit>true</Prefer32Bit>
+ <OtherFlags />
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="mscorlib" />
+ <Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+ <Private>True</Private>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Numerics" />
+ <Reference Include="Eto">
+ <HintPath>..\packages\Eto.Forms\lib\net45\Eto.dll</HintPath>
+ </Reference>
+ <Reference Include="Eto.Gtk3">
+ <HintPath>..\packages\Eto.Platform.Gtk3\lib\net45\Eto.Gtk3.dll</HintPath>
+ </Reference>
+ <!-- GTK 3 -->
+ <Reference Include="atk-sharp">
+ <HintPath>..\packages\GtkSharp\lib\net45\atk-sharp.dll</HintPath>
+ </Reference>
+ <Reference Include="cairo-sharp">
+ <HintPath>..\packages\GtkSharp\lib\net45\cairo-sharp.dll</HintPath>
+ </Reference>
+ <Reference Include="gdk-sharp">
+ <HintPath>..\packages\GtkSharp\lib\net45\gdk-sharp.dll</HintPath>
+ </Reference>
+ <Reference Include="gio-sharp">
+ <HintPath>..\packages\GtkSharp\lib\net45\gio-sharp.dll</HintPath>
+ </Reference>
+ <Reference Include="glib-sharp">
+ <HintPath>..\packages\GtkSharp\lib\net45\glib-sharp.dll</HintPath>
+ </Reference>
+ <Reference Include="gtk-sharp">
+ <HintPath>..\packages\GtkSharp\lib\net45\gtk-sharp.dll</HintPath>
+ </Reference>
+ <Reference Include="pango-sharp">
+ <HintPath>..\packages\GtkSharp\lib\net45\pango-sharp.dll</HintPath>
+ </Reference>
+ <Reference Include="Npgsql">
+ <HintPath>..\packages\npgsql\lib\net45\npgsql.dll</HintPath>
+ </Reference>
+ <Reference Include="FsControl.dll">
+ <HintPath>..\packages\FsControl\lib\net40\FsControl.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="App.config" />
+ <Compile Include="EtoUtils.fs" />
+ <Compile Include="Sensor.fs" />
+ <Compile Include="Program.fs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <Choose>
+ <When Condition="'$(VisualStudioVersion)' == '11.0'">
+ <PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets')">
+ <FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
+ </PropertyGroup>
+ </When>
+ <Otherwise>
+ <PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets')">
+ <FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
+ </PropertyGroup>
+ </Otherwise>
+ </Choose>
+ <Import Project="$(FSharpTargetsPath)" Condition="Exists('$(FSharpTargetsPath)')" />
+ <Import Project="..\packages\GtkSharp\build\net45\GtkSharp.targets" Condition="Exists('..\packages\GtkSharp\build\net45\GtkSharp.targets')" />
+</Project>
\ No newline at end of file
--- /dev/null
+module PiNet
+
+open System
+open System.Collections.Generic
+open Eto
+open Eto.Forms
+open Eto.Drawing
+open EtoUtils
+open FsControl.Operators
+
+type Tree<'t> =
+ | Tree of 't * Tree<'t> * Tree<'t>
+ | Leaf of 't
+ static member Map (x:Tree<'a>, f) =
+ let rec loop f = function
+ | Leaf x -> Leaf (f x)
+ | Tree (x, t1, t2) -> Tree (f x, loop f t1, loop f t2)
+ loop f x
+
+[<EntryPoint>]
+let main argv =
+ printfn "PiNet starting ..."
+
+ let myTree = Tree(6, Tree(2, Leaf 1, Leaf 3), Leaf 9)
+ printfn "tree: %A" <| map float myTree
+
+ use app = new Application()
+ use form = new Form(Title = "Hello world", Topmost = true, Size = Size(320, 240))
+ let mutable counter = 0
+ let button = new Button(Text = "+1")
+ let display = new Label(Text = string counter)
+ let table = Tbl [ Row [ El display ]; Row [ El button ] ]
+ let updateCounter c = counter <- c; display.Text <- string c
+ button.Click.Add(fun _ -> updateCounter (counter + 1))
+ form.Content <- table |> makeLayout
+ form.Show()
+
+ app.Run(form)
+
+ // let asyncBuilder = Sensor.start ()
+ // asyncBuilder |> Async.RunSynchronously
+
+ printfn "PiNet stopped"
+
+ 0
+
--- /dev/null
+module Sensor
+
+open System
+open System.Runtime.InteropServices
+open Npgsql
+
+module Data =
+ [<DllImport("sensor.so", CallingConvention = CallingConvention.Cdecl)>]
+ extern void getHumidityAndTemperature(double& h, double& t)
+
+ [<DllImport("sensor.so", EntryPoint = "init", CallingConvention = CallingConvention.Cdecl)>]
+ extern bool init()
+
+let interval = 10000. // [ms].
+
+let start () =
+ printfn "Sensor service starting ..."
+
+ // Initialize the sensor.
+ if not <| Data.init()
+ then
+ failwith "Unable to initialize sensor"
+
+ let conn = new NpgsqlConnection("Host=192.168.1.6;Username=pinet;Password=nidKuds7;Database=pinet")
+ conn.Open()
+
+ let cmdInsert =
+ new NpgsqlCommand(
+ """INSERT INTO sensor_measure
+ (datetime, humidity, temperature)
+ VALUES (@d, @h, @t)""", conn)
+
+ async {
+ while true do
+ let time = DateTime.UtcNow
+
+ let humidity, temp =
+ let mutable h = 0.0
+ let mutable t = 0.0
+ Data.getHumidityAndTemperature(&h, &t)
+ h, t
+
+ cmdInsert.Parameters.Clear()
+ cmdInsert.Parameters.AddWithValue("@d", DateTime.UtcNow) |> ignore
+ cmdInsert.Parameters.AddWithValue("@h", humidity) |> ignore
+ cmdInsert.Parameters.AddWithValue("@t", temp) |> ignore
+
+ if cmdInsert.ExecuteNonQuery() <> 1
+ then
+ printfn "Unable to insert values into the DB"
+
+ let delta = (DateTime.UtcNow - time).TotalMilliseconds
+ let toWait =
+ let t = interval - delta
+ int <| if t < 0. then 0. else t
+
+ do! Async.Sleep(toWait)
+ }
+
+ (*use cmd = new NpgsqlCommand("SELECT id, datetime, humidity, temperature FROM sensor_measure", conn)
+
+ use reader = cmd.ExecuteReader()
+ while reader.Read() do
+ printfn
+ "id: %d, datetime: %A, humidity: %.1f, temperature: %.1f"
+ (reader.GetInt32(0)) (reader.GetDateTime(1)) (reader.GetFloat(2)) (reader.GetFloat(3))
+ 0
+
+
+ printfn "Humidity: %.1f, Temperature: %.1f" humidity temp
+
+ 0*)
\ No newline at end of file
--- /dev/null
+#!/bin/bash
+g++ --std=c++14 -O2 -shared -o libsensor.so main.cpp -l bcm2835
+cp libsensor.so ../PiNet/bin/Release/
--- /dev/null
+/*
+ * Author: greg.burri@gmail.com
+ * libbcm2835 documentation : http://www.airspayce.com/mikem/bcm2835/index.html
+ */
+
+#include <bcm2835.h>
+#include <iostream>
+#include <ctime>
+using namespace std;
+
+const auto pin = RPI_GPIO_P1_07;
+const double temperatureMax = 60;
+const double temperatureMin = -20;
+const double humidityMax = 100;
+const double humidityMin = 0;
+
+class ReadException {};
+
+inline uint64_t timeFrom(clock_t t)
+{
+ return uint64_t(clock() - t) / (CLOCKS_PER_SEC / 1000 / 1000);
+}
+
+void sync(uint64_t timeout, bool toUp)
+{
+ auto last = bcm2835_gpio_lev(pin);
+ bool synced = false;
+
+ clock_t t = clock();
+
+ while (!synced && timeFrom(t) < timeout)
+ {
+ auto current = bcm2835_gpio_lev(pin);
+ synced = toUp ? (last == LOW && current == HIGH) : (last == HIGH && current == LOW);
+ last = current;
+ }
+
+ if (!synced)
+ {
+ // uint64_t delta = timeFrom(t);
+ // cout << "Unable to sync " << (toUp ? "up" : "down") << " Time: " << delta << endl;
+ throw ReadException();
+ }
+}
+
+inline void syncToUp(long timeout)
+{
+ sync(timeout, true);
+}
+
+inline void syncToDown(long timeout)
+{
+ sync(timeout, false);
+}
+
+bool readNextBit()
+{
+ syncToUp(300); // Should be 50 us.
+ clock_t t = clock();
+ syncToDown(300); // Should be 70 us.
+ return timeFrom(t) > 50;
+}
+
+uint8_t readNextByte()
+{
+ uint8_t result = 0;
+ for (int i = 0; i < 8; i++)
+ if (readNextBit())
+ result |= (1 << (7 - i));
+ return result;
+}
+
+extern "C"
+bool init()
+{
+ if (!bcm2835_init())
+ {
+ cerr << "Unable to init the BCM 2835" << endl;
+ return false;
+ }
+ return true;
+}
+
+extern "C"
+void getHumidityAndTemperature(double& h, double& t)
+{
+ bool dataOK = false;
+ while (!dataOK)
+ {
+ try
+ {
+ bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_OUTP);
+
+ bcm2835_gpio_write(pin, LOW);
+ bcm2835_delayMicroseconds(30000); // 30 ms.
+ bcm2835_gpio_write(pin, HIGH);
+ bcm2835_delayMicroseconds(30);
+
+ bcm2835_gpio_fsel(pin, BCM2835_GPIO_FSEL_INPT);
+
+ syncToUp(200); // Should be 80 us.
+ syncToDown(200); // Should be 80 us.
+
+ int rh1 = readNextByte();
+ int rh2 = readNextByte();
+ int t1 = readNextByte();
+ int t2 = readNextByte();
+ int checksum = readNextByte();
+
+ if (((rh1 + rh2 + t1 + t2) & 0xFF) != checksum)
+ throw ReadException();
+
+ h = double((rh1 << 8) | rh2) / 10.0;
+ t = double(((t1 & 0x7F) << 8) | t2) / 10.0;
+
+ if (t1 & 0x80)
+ t *= -1.0;
+
+ if (h >= humidityMin && h <= humidityMax && t >= temperatureMin && t <= temperatureMax)
+ {
+ dataOK = true;
+ }
+ }
+ catch(ReadException ex)
+ {
+ cerr << "Read error, retrying ..." << endl;
+ }
+
+ if (!dataOK)
+ bcm2835_delay(2000);
+ }
+}
--- /dev/null
+#!/usr/bin/fsharpi
+
+#I "packages/FAKE/tools/"
+#r @"packages/FSharp.Compiler.Service/lib/net45/FSharp.Compiler.Service.dll"
+#r @"packages/FAKE/tools/FakeLib.dll"
+
+open System.Diagnostics
+open Fake
+open Fake.EnvironmentHelper
+
+let buildDir = "./build/"
+
+// To match the beginning of a string and extract the rest.
+let (|Prefix|_|) (p: string) (s: string) =
+ if s.StartsWith(p) then
+ Some(s.Substring(p.Length))
+ else
+ None
+
+let target =
+ match Array.tryPick (fun a -> match a with Prefix "target=" target -> Some target | _ -> None) fsi.CommandLineArgs with
+ | Some target -> target
+ | _ -> "Debug"
+
+let shellExecute program args =
+ let startInfo = new ProcessStartInfo()
+ startInfo.FileName <- program
+ startInfo.Arguments <- args
+ startInfo.UseShellExecute <- true
+
+ let proc = Process.Start(startInfo)
+ proc.WaitForExit()
+ ()
+
+Paket.Restore (fun parameters -> parameters)
+
+Target "Clean" (fun _ ->
+ trace "Cleaning..."
+ CleanDir buildDir
+)
+
+Target "Debug" (fun _ ->
+ trace "Building in Debug mode..."
+ !! "**/*.fsproj" |> MSBuildDebug buildDir "Build" |> Log "Debug-Output:"
+)
+
+Target "Release" (fun _ ->
+ trace "Building in Release mode..."
+)
+
+Target "Deploy" (fun _ ->
+ trace "Deployement..."
+)
+
+// "Clean" ==> "Debug"
+"Clean" ==> "Release" ==> "Deploy"
+
+Run target
--- /dev/null
+#!/usr/bin/env bash
+./build.fsx
--- /dev/null
+#!/usr/bin/env bash
+./build.fsx target=Release
+
--- /dev/null
+#!/usr/bin/env bash
+./build.fsx target=Debug && mono build/PiNet.exe
+
--- /dev/null
+source https://www.nuget.org/api/v2
+
+nuget FSharp.Compiler.Service
+nuget Npgsql
+nuget FAKE
+nuget Eto.Forms
+nuget Eto.Platform.Gtk3
+nuget GtkSharp
+nuget FsControl >= 2.0.0-CI00087