Wednesday, November 2, 2016

Automatic watering system using Gardena 1197 + OpenHab + Pilight

I was tired of having the repetitiveness of watering my garden every day so I wanted a solution to manage the garden watering. It was not as simple as just using one circuit because my garden is spread out, so different areas need different times and water pressure is a problem. I also did not want to dig my garden up to lay down new water pipes, so I wanted to be able to use my existing garden hose's.

The solution cost was to use Gardena automatic water distributor along with an single electronic valve from Hunter. This keeps the electrics simple and the connections plug and play. The control was done with openhab running on a raspberry pi, which triggered the valve via a remote 433mhz switch. You could obviously use a valve per circuit, but this requires another bridge to the PI and I had the 433Mhz already set up with pilight. As it is below you can also switch the valve with an electric pump, which I sometimes do when I have collected enough rain water.

Parts list:
1.) Remote switch - 25 Euro - Although I had this already
2.) Gardena automatic water distributer - 55 Euro
3.) 24 V AC Transformer - 12 Euro
4.) Hunter PGV-100 valve - 16 Euro - basically cheaper than Gardena version
5.) Raspberry PI - 40 Euro - although had this already as well
6.) Aurel 433Mhz transmitter
7.) Various garden houses to different sections of the garden, 4 circuits in use, but up to 6 can be used


View of the switch, the transformer and hunter valve

Garden Distributor
The Gardena distributor works quite simply by switching to the next circuit when there is no water pressure for a certain period of time (I was using about 10 seconds). So with a on-off-on-etc pattern you can control up to 6 circuits with different time periods if you want.

View of control panel in Openhab basic UI
Pilight needs to be installed and the pilight binding needs to be setup inside openhab

addons.cfg:
    pilight:kaku.host=192.168.2.168
    pilight:kaku.port=5000
    pilight:kaku.delay=1000

Pilight devices:
     "devices": {
                "desklamp": {
                        "protocol": [ "kaku_switch" ],
                        "id": [{
                                "id": 15831690,
                                "unit": 1
                        }],
                        "state": "off"              
        },

Items file:
Switch  KakuDeskLamp    "Water"               {pilight="kaku#desklamp"}
Switch  WaterTest    "Water Test"
Switch  StartProgram    "Start Program"
Switch  KakuSmallBathFan    "Small bath fan"               {pilight="kaku#smallbathfan"}
Number      Irrigation_LawnMins     "Lawn Sprinkler [%d mins]"  <water>         (Irrigation)
Number      Irrigation_Pause     "Break [%d mins]"  <water>         (Irrigation)
Number      Irrigation_Multiplier     "Multiplier [%d mins]"  <water>         (Irrigation)
Number      Irrigation_Circuits     "Circuits [%d]"  <water>         (Irrigation)
Number      Irrigation_ActiveCircuit     "Circuits [%d]"  <water>         (Irrigation)

Number      Irrigation_Repeats     "Repeats [%d]"  <water>         (Irrigation)

Openhab rules:
    rule "Enabling irrigation"
    when
    Item StartProgram changed from OFF to ON or
    Time cron "0 45 7 1/1 * ? *"
    then
      var i = 0
      var Number lawnMins = Irrigation_LawnMins.state as DecimalType
      var Number pause = Irrigation_Pause.state as DecimalType
      var Number Multiplier = Irrigation_Multiplier.state as DecimalType
      var Number Circuits = Irrigation_Circuits.state as DecimalType
      var Number Repeats = Irrigation_Repeats.state as DecimalType
      lawnMins = lawnMins*1000
      if (Multiplier != 0)
      {
        lawnMins = lawnMins*Multiplier
      }
      logInfo("Irrigation", "Circuits " + Circuits + " multiplier " + Multiplier)
      while ((i=i+1) < Circuits+1) {
        var j = 0
        while ((j=j+1) < Repeats+1) {
                logInfo("Irrigation", "Repeat " + j)
                sendCommand(KakuDeskLamp, ON)
                Thread::sleep(100)
        }
    
        logInfo("Irrigation", i + " lawnMins " + lawnMins)
        sendCommand(Irrigation_ActiveCircuit, i)
        Thread::sleep(lawnMins.intValue) //*lawnMins)
        j = 0
        while ((j=j+1) < Repeats+20) {
                logInfo("Irrigation", "Repeat " + j)
                sendCommand(KakuDeskLamp, OFF)
                Thread::sleep(100)
        }
   
        sendCommand(Irrigation_ActiveCircuit, 0)
    
        Thread::sleep(1000*pause.intValue)
      }
      sendCommand(StartProgram, OFF)
    end


Thursday, December 17, 2015

Qt issue /problem with frameless window flickering on ubuntu

When using a frameless window in QML (prehaps directly from c++ as well) you need to manually allow dragging of the window. The first solution I found was like this:

Window  {
    id: appWindow
    flags: Qt.Window|Qt.FramelessWindowHint

    MouseArea {
        id: mouseRegion
        anchors.fill: parent;
        property variant clickPos: "1,1"

        onPressed: {
            clickPos  = Qt.point(mouse.x,mouse.y)
        }

        onPositionChanged: {
            var delta = Qt.point(mouse.x-clickPos.x, mouse.y-clickPos.y)
            appWindow.x += delta.x;
            appWindow.y += delta.y;
        }
    }
}

This worked on windows, but on Ubuntu the window flickered on Ubuntu due to a race condition:
Mouse move event A:
                Receive mouse position relative to app position Y
                Update app to position X

Mouse move event B:
                Receive mouse position relative to old app position Y (and not X as expected)
                Update app to position Z

The second effectively incorrectly does “double” the delta…


The fix is then to use a global cursor position as reference, so the new solution is then

Window  {
    id: appWindow
    flags: Qt.Window|Qt.FramelessWindowHint
    MouseArea {
        id: mouseRegion
        anchors.fill: parent;
        property variant clickPos: "1,1"
        property variant appStartPos: "1,1"

        onPressed: {
            // fetch global position due to bug on ubuntu
            clickPos = gApplicationController.getCursorPos()
            appStartPos = Qt.point(appWindow.x, appWindow.y)
        }

        onPositionChanged: {
            var newPos = gApplicationController.getCursorPos()
            var delta = Qt.point(newPos.x-clickPos.x, newPos.y-clickPos.y)
            appWindow.x = appStartPos.x+delta.x;
            appWindow.y = appStartPos.y+delta.y;
        }
    }
}

Note you need to set up a c++ class that gets the cursor position something like

class AppController : public QObject
{
    Q_OBJECT

    QQmlApplicationEngine m_engine;
public:
    Q_INVOKABLE QVariant getCursorPos();
}

AppController::AppController()
{
    m_engine.rootContext()->setContextProperty("gApplicationController", this);
}

QVariant AppController::getCursorPos()
{
    return QVariant(QCursor::pos());
}

Friday, November 27, 2015

Running ubuntu/linux on a xen server 6.5 with a passthrough GPU

For a while I was struggling to get ubuntu to work with my NVIDIA GPU in passthrough mode on xenserver. Xorg start, but then just fail... and those xorg messages are sooo helpful ;). Any way it turns out xorg could not correctly map the right driver to the correct PCI device. I had to add the PCI bus ID to the xorg.conf like here:

 BusID "PCI:0:12:0"

 Found this info here: https://wiki.archlinux.org/index.php/Xorg#More_than_one_graphics_card:

 my devices "lspci | grep -e VGA -e 3D":

00:02.0 VGA compatible controller: Cirrus Logic GD 5446
00:06.0 VGA compatible controller: NVIDIA Corporation GK104GL [Quadro K5000] (rev a1)

Sunday, February 1, 2015

Temperature and Relative Humidity

Below is a plot of my measured temperatures and humitidy for my house over time. Data is collected from Oregon sensors over 433hz wireless transmission to the raspberry pi (with 433hz receiver) and stored in Google docs. The graph below is created with the help of highcharts using java script.

12/12/2014

give me input

Sunday, January 25, 2015

Curl C to google sheets

A quick post about using the CURL C API to upload data to google spreadsheets. I used it upload temperature and humidity data that I am tracking at home. There are a few steps

1.) create a google spreadsheet the normal way (with headings for each data item you want to save)

2.) get auth from google:
                curl_easy_setopt(curl, CURLOPT_URL, "https://www.google.com/accounts/ClientLogin");
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "Email=YOUREMAIL&Passwd=YOURPASSWORD&accountType=GOOGLE&source=cURL&service=writely");

3.) Form your data in the correct XML format:
      sprintf(postData, "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gsx='http://schemas.google.com/spreadsheets/2006/extended'> <gsx:date>%s</gsx:date>  <gsx:time>%s</gsx:time>  <gsx:temp>%f</gsx:temp>  <gsx:relhum>%f</gsx:relhum><gsx:location>%d</gsx:location><gsx:device>oregon</gsx:device></entry>", 
dateStr, timeStr, s->getTemperature(), s->getHumidity(), s->getChannel());

4.) Post the data with the correct fields to the spreadsheet:
                        curl_easy_setopt(curl, CURLOPT_URL, "https://spreadsheets.google.com/feeds/list/YOURSPREADSHEETKEY/1/private/full");
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(postData));
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData);
curl_easy_setopt(curl, CURLOPT_POST, true);

struct curl_slist *headerlist=NULL;
char authHeader[600];
sprintf(authHeader, "Authorization: GoogleLogin auth=%s", g_auth);
cout << "auth header " << authHeader << endl << flush;
headerlist = curl_slist_append(headerlist, authHeader);
headerlist = curl_slist_append(headerlist, "GData-Version: 3.0");
headerlist = curl_slist_append(headerlist, "Content-Type: application/atom+xml");


Full function code at https://github.com/arcanon/curlgooglespreadsheet/blob/master/googledocs-c.cpp

Monday, February 24, 2014

Modifying coova chilli to allow anonymous users for a private hotspot using automatic MAC addresses

So I spent a lot of time looking for a way to allow anonymous users to a coova chilli hotspot. Anonymous in the sense that a user can just click a button and get 30 min Internet access without having to type a password and what not. It made complete sense to use the MAC address as the users ID, but this only worked if you entered the MAC already. And NO where did I find that you could accept any new MAC address as a new user. Eventually I learnt enough about coova chilli to do this myself. The trick is to change the function dologin in the haserl script /etc/chilli/www/config-local.sh (use your password instead of XXXX) to


dologin() {
    res="$(echo "select username from radcheck where username ='$REMOTE_MAC';" | mysql -u root -pXXXX radius)"
    if [ "$res" = "" ]
    then
        echo "insert into radcheck (username, attribute, op, value) values ('$REMOTE_MAC', 'Cleartext-Password', ':=', 'password');" | mysql -u root -pXXXX radius
        echo "insert into radusergroup (username, groupname) values ('$REMOTE_MAC', '30min');" | mysql -u root -pXXXX radius
    fi
#    url=$(chi_login_url "$FORM_username" "$FORM_password" "$FORM_userurl")
    url=$(chi_login_url "$REMOTE_MAC" "password" "$FORM_userurl")
    cat <<ENDHTML
<html><head>
<meta http-equiv="refresh" content="0;url=$url"/>
</head></html>
ENDHTML
    wisprLoginResultsURL "$url" 
}


Once you have done this, every time a new device logs into your web page, the script will check if the MAC address exists or not. If not it will require any username. I changed my login page to just be a button and made the other fields hidden, like so edit /etc/chilli/www/login_form.tmpl :

<!--
  --  The login form
  -->

<div id="login-form">
<table>
<tr>
<!--  <td>Usernamebb</td> -->
  <td><INPUT NAME="username" VALUE="user1" TYPE="hidden"></td>
</tr>
<tr>
<!--  <td>Password</td> -->
  <td><INPUT NAME="password" VALUE="password" TYPE="hidden" TYPE="password"></td>
</tr>
<tr>
  <td colspan="2" nowrap align="center">
    <input type="submit" name="button" value="Login & Accept Terms">
  </td>
</tr>
</table>
</div>



Wednesday, February 12, 2014

Hacking a hermes pro router to get a isdn line working

So I had an issue that a Hermes Pro/S+ router died. This is used by pharmacy software (more specifically Apotheken Software) to get order supplies from the supplier. Now of course when we asked the support company for some help, they immediately said that would be 1000 euros please. In fact they did not want to tell us the price. They just wanted to charge directly. And they did not offer any other options.

So instead i ordered a Hermes Pro/SH online from ebay. So it arrived. Cool! Then i found out that it did not have an IP written down. For anyone that may know, that means its not really easily configurable. The next way to do this was over the V24 port. This is a serial port and should connect easily to your PCs COM port. That's if u still have one:). Basically a Ethernet to 9 pin serial port cable is necessary. Now looking online, they did exist, but that meant ordering a cable and we did not really have the time for that. So a i decided to make my own. Just cut open a Ethernet cable and join it to 9 pin serial cable (USB adapter which i bought for my laptop).

So i did this following the standard that exists for Ethernet to 9 pin serial. Then i fired up putty to telnet into the router, but nothing:( my heart sank. So much time and still no solution. And the business needs this to run properly...  so i head home a bit unhappy. On the way i remember one point of data i got from the sales guy. That was that the Hermes router was built from an old Cisco router. So i start googling a Cisco V24 port router. I find a page detailing the mapping for Cisco. And what do u know, its not the same as the standard that i tried before! So i do some rewiring and fire up putty again. I have never been so happy to see a login screen! This was the result: