First commit. master
authorUmmon <greg.burri@gmail.com>
Sun, 3 Jul 2016 19:58:12 +0000 (21:58 +0200)
committerUmmon <greg.burri@gmail.com>
Sun, 3 Jul 2016 19:58:12 +0000 (21:58 +0200)
15 files changed:
.gitignore [new file with mode: 0644]
PiNet/App.config [new file with mode: 0644]
PiNet/AssemblyInfo.fs [new file with mode: 0644]
PiNet/EtoUtils.fs [new file with mode: 0644]
PiNet/PiNet.fsproj [new file with mode: 0644]
PiNet/Program.fs [new file with mode: 0644]
PiNet/Sensor.fs [new file with mode: 0644]
Sensor/AM2302.pdf [new file with mode: 0644]
Sensor/build.sh [new file with mode: 0644]
Sensor/main.cpp [new file with mode: 0644]
build.fsx [new file with mode: 0755]
build.sh [new file with mode: 0755]
build_deploy.sh [new file with mode: 0755]
build_run.sh [new file with mode: 0755]
paket.dependencies [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..2a89803
--- /dev/null
@@ -0,0 +1,8 @@
+.fake/
+.paket/
+paket.lock
+packages/
+*.so
+obj
+bin
+build/
\ No newline at end of file
diff --git a/PiNet/App.config b/PiNet/App.config
new file mode 100644 (file)
index 0000000..3661651
--- /dev/null
@@ -0,0 +1,6 @@
+<?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
diff --git a/PiNet/AssemblyInfo.fs b/PiNet/AssemblyInfo.fs
new file mode 100644 (file)
index 0000000..108b5ac
--- /dev/null
@@ -0,0 +1,41 @@
+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
diff --git a/PiNet/EtoUtils.fs b/PiNet/EtoUtils.fs
new file mode 100644 (file)
index 0000000..f6a9427
--- /dev/null
@@ -0,0 +1,40 @@
+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
diff --git a/PiNet/PiNet.fsproj b/PiNet/PiNet.fsproj
new file mode 100644 (file)
index 0000000..0b16902
--- /dev/null
@@ -0,0 +1,108 @@
+<?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
diff --git a/PiNet/Program.fs b/PiNet/Program.fs
new file mode 100644 (file)
index 0000000..5650def
--- /dev/null
@@ -0,0 +1,46 @@
+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
+
diff --git a/PiNet/Sensor.fs b/PiNet/Sensor.fs
new file mode 100644 (file)
index 0000000..8ae206f
--- /dev/null
@@ -0,0 +1,72 @@
+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
diff --git a/Sensor/AM2302.pdf b/Sensor/AM2302.pdf
new file mode 100644 (file)
index 0000000..a2e3d52
Binary files /dev/null and b/Sensor/AM2302.pdf differ
diff --git a/Sensor/build.sh b/Sensor/build.sh
new file mode 100644 (file)
index 0000000..0017f6d
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+g++ --std=c++14 -O2  -shared -o libsensor.so main.cpp -l bcm2835
+cp libsensor.so ../PiNet/bin/Release/
diff --git a/Sensor/main.cpp b/Sensor/main.cpp
new file mode 100644 (file)
index 0000000..ec6b98a
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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);
+   }
+}
diff --git a/build.fsx b/build.fsx
new file mode 100755 (executable)
index 0000000..a048f02
--- /dev/null
+++ b/build.fsx
@@ -0,0 +1,58 @@
+#!/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
diff --git a/build.sh b/build.sh
new file mode 100755 (executable)
index 0000000..5d80e23
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,2 @@
+#!/usr/bin/env bash
+./build.fsx
diff --git a/build_deploy.sh b/build_deploy.sh
new file mode 100755 (executable)
index 0000000..230aa7c
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+./build.fsx target=Release
+
diff --git a/build_run.sh b/build_run.sh
new file mode 100755 (executable)
index 0000000..b9c7a64
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+./build.fsx target=Debug && mono build/PiNet.exe
+
diff --git a/paket.dependencies b/paket.dependencies
new file mode 100644 (file)
index 0000000..d10d468
--- /dev/null
@@ -0,0 +1,9 @@
+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