Skip to content
This repository has been archived by the owner on Aug 13, 2020. It is now read-only.

SensorKit examples are not working #76

Closed
peiworld opened this issue Jul 15, 2017 · 7 comments
Closed

SensorKit examples are not working #76

peiworld opened this issue Jul 15, 2017 · 7 comments

Comments

@peiworld
Copy link

Do I need to run the example of SensorKit using sudo?

trigger : std.Trigger(tick=1, data=true)
light: io.Light()
trigger.data > light.on

When I tried to run the light example, without sudo I got the following error messages. With sudo, no response and the LED is not on.

2017-07-15 00:54:13,241 INFO 6941-calvin.calvin.runtime.north.calvincontrol: Control API trying to listening on: localhost:5001
2017-07-15 00:54:14,826 ERROR 6941-calvin.calvin.runtime.north.actormanager: _new
Traceback (most recent call last):
File "/home/pi/calvin-base/calvin/runtime/north/actormanager.py", line 138, in _new
a.init(**args)
File "/home/pi/calvin-base/calvin/actorstore/systemactors/io/Light.py", line 30, in init
self.setup()
File "/home/pi/calvin-base/calvin/actorstore/systemactors/io/Light.py", line 33, in setup
self.light = calvinsys.open(self, "io.light")
File "/home/pi/calvin-base/calvin/actor/actor.py", line 208, in open
return get_calvinsys().open(name, actor, **kwargs)
File "/home/pi/calvin-base/calvin/runtime/north/calvinsys.py", line 70, in open
pymodule = importlib.import_module('calvin.runtime.south.calvinsys.' + capability['path'])
File "/usr/lib/python2.7/importlib/init.py", line 37, in import_module
import(name)
File "/home/pi/calvin-base/calvin/runtime/south/calvinsys/io/gpiopin/raspberry_pi/PIGPIOPin.py", line 19, in
import pigpio
ImportError: No module named pigpio

Also, I tried humidity.calvin with an DHT11. Nothing print out when I used sudo.
pi@raspberrypi:~/calvin-base/calvin/examples/sensor-kit$ sudo csruntime --host localhost -w 0 humidity.calvin

By the way, the pin layout looks wrong. The GND of the sensor is connected at GPIO:21 not an GND pin.

@persquare
Copy link
Contributor

Do I need to run the example of SensorKit using sudo?

No. You should never have to run any of the commands using sudo.

When I tried to run the light example, without sudo I got the following error messages.
[snip]
import pigpio
ImportError: No module named pigpio

For some reason, the pigpio package is not installed on the device.

Also, I tried humidity.calvin with an DHT11. Nothing print out when I used sudo.
pi@raspberrypi:~/calvin-base/calvin/examples/sensor-kit$ sudo csruntime --host localhost -w 0 humidity.calvin

Again, you shouldn't use sudo, but the example should work.

By the way, the pin layout looks wrong. The GND of the sensor is connected at GPIO:21 not an GND pin.

Thanks for pointing that out, that is most likely a bug on our behalf.

I'll look into the above issues.

@peiworld
Copy link
Author

peiworld commented Jul 22, 2017

Thanks, I have two DS18B20 temperature sensors attached. So I am modifying the code (see below) to grab sensor readings from those.

However, I feel that this is not a right way of doing it, neither the original code which reads only the first sensor. I think the sensorID should be passed in as an argument. Right?

Problem with the following code:

  • I cannot use dict within the _read_temp function. It never get to this "print "dict:"+len(temps)"
  • I cannot use appending of string such as temp
    self._temperature+=self._temperature+tmp_temperature
    Is there a syncrhoised block declaration or multithread lock?
# -*- coding: utf-8 -*-

# Copyright (c) 2017 Ericsson AB
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from calvin.runtime.south.calvinsys.io.ds18b20thermometer.BaseDS18B20 import BaseDS18B20
from calvin.utilities.calvinlogger import get_logger
from calvin.runtime.south.plugins.async import async
from os import path
import glob
import json

_log = get_logger(__name__)

class DS18B20(BaseDS18B20):
    """
    Calvinsys object handling DS18B20 temperature sensor
    """
    def init(self, **kwargs):
        self._base_dir = '/sys/bus/w1/devices/'
        self._temperature = None
        self._device_folders = self._find_device_folders()
        self._in_progress = None

    def _find_device_folders(self):
        try :
            device_folders = glob.glob(self._base_dir + '28*')
        except Exception as e:
            _log.warning("Failed to find device file: {}".format(e))
            device_folders = None
        return device_folders
        

    def _read_temp_raw(self, device_file):
        try:
            with open(device_file, 'r') as fp:
                return fp.readlines()
        except:
            return None

    def _read_temp(self):
        temps={}
        for device_folder in self._device_folders:
            lines = self._read_temp_raw(device_folder+'/w1_slave')
            if not lines or lines[0].strip()[-3:] != 'YES':
                # Nothing to read, try again in a second
                self._in_progress = async.DelayedCall(1.0, self._read_temp)
                return

            equals_pos = lines[1].find('t=')
            tmp_temperature=None
            sensorID=path.basename(path.normpath(device_folder))
            if equals_pos != -1:
                temp_string = lines[1][equals_pos + 2:]
                print device_folder+" "+sensorID+" "+temp_string
                tmp_temperature = str(float(temp_string)/1000.0)
                print "test:"+tmp_temperature
            else :
                print "waiting..."
                self._in_progress = async.DelayedCall(1.0, self._read_temp)
        
            # clear in_progress
            self._in_progress = None
            print "test2:" + tmp_temperature
            temps[sensorID]= tmp_temperature
        print "dict:"+len(temps)
        self._temperature = json.dumps(temps)


    def _start_read(self):
        print "_start_read"
        async.call_from_thread(self._read_temp)

    def can_write(self):
        print "can_write"
        return self._in_progress is None

    def write(self, measure):
        print"write"
        self._in_progress = async.DelayedCall(0.0, self._read_temp)

    def can_read(self):
        print "can_read"
        return self._temperature is not None
        
    def read(self):
        print "read"
        _temperature = self._temperature
        self._temperature = None
        return _temperature
        
    def close(self):
        print "close"
        self._device_folders = None

@persquare
Copy link
Contributor

Thanks, I have two DS18B20 temperature sensors attached. So I am modifying the code (see below) to grab sensor readings from those.
However, I feel that this is not a right way of doing it, neither the original code which reads only the first sensor. I think the sensorID should be passed in as an argument. Right?

Your intuition is correct, there is another way of doing it, and I'm afraid that the explanation is a bit lengthy so bare with me :)

I'll start with an example relevant to your setup:
Assume that we have some pipe system with water flowing from an inlet to an outlet, and we want to measure the temperature at those sites to establish how much energy was lost in the process.

To do that, we need a temperature actor on each site which of course requires us to have a runtime at each site. Actors are completely ignorant of where they execute, so all information about location etc. is contained within the runtime. For simplicity's sake, lets assume that we have given the runtimes the names "inlet" and "outlet", respectively. Using deployment rules, Calvin will deploy a temperature actor on each runtime:

# Incomplete code illustrating deployment rules
t_in : sensor.Temperature()
t_out : sensor.Temperature()
...
apply t_in : node_name("inlet")
apply t_out : node_name("outlet")

The reason for this is that we eventually want sensors to be calvin-compatible units themselves, rather than dumb devices tied to a gateway. So, this is a conscious design decision that we hope will pay off in the long run.
(It makes sense to think about using two battery powered constrained devices communicating over WiFi or BluetoothLE, each having a DS18B20 sensor and running calvin-constrained runtimes.)

What trips things up here is the fact that e.g. a Raspberry Pi is a cheap device capable of handling a multitude of sensors that powerful and very easy to work with. But it is not the kind of device that Calvin was designed for... So while the obvious solution to hook up two sensors with some length of wire to the same RPi is tempting, the "proper" way is to hook up one sensor per runtime, and instantiate two runtimes with different attributes (names "inlet" and "outlet" in this case) and your OK to go.

Actually, you will need two separate Raspberry Pis to make this work, since the driver won't let two runtimes share access to the same sensor. It's in our roadmap to implement a "virtual runtime" to let a single RPi handle this kind of situation.

So, in summary, in addition to being a substrate where actors can execute, a runtime (through its unique set of attributes) represents the physical location of a sensor, and it doesn't make sense to have two identical sensors at the same physical location.

@peiworld
Copy link
Author

In our modeling, we assume each IoT node will run only one runtime. Each runtime seems to take up to 32MB in RAM. Firing up multiple runtimes seems not ideal even for relatively constrained devices. However, can we achieve ideas like sandboxing if multiple actors running in the same runtime.

I understand that we normally don't have multiple attachments of the same type of sensor at the same location. But in some cases, we may have such deployment situation.

@persquare
Copy link
Contributor

Have you looked at calvin-constrained https://github.com/EricssonResearch/calvin-constrained? IIRC, the x86 port is (despite its name) a generic ANSI C port and should run on RPi "as is". It shouldn't use more than a 100k or so of RAM. Having two of them, each having a temp sensor, connected to a "host" runtime would allow each sensor to have it's set of attributes etc. We've used this setup to create "proxy-runtimes" for multiple devices connected to RPis.
That said, there is no calvinsys implementation of the ds18b20 driver for the x86 port, but there is one for the ESP8266 (which is what we use most of the time) that could serve as an example.

(It's summer vacation time over here, so responses and definitive answers might be in short supply until mid August I'm afraid...)

@olaan
Copy link
Contributor

olaan commented Aug 7, 2017

As the author of the issue noted, there is currently no way of specifying which sensor to use if several are present - the runtime will presently always select the first one encountered.

It is straightforward to add support for indexing based on some ordering (e.g. the serial number of the sensor.) This would then be used in the configuration file to either specify a sensor to use for the io.temperature capability, or assign different capabilities to each sensor. The latter would then require the application use different actors for the different sensors rather than using different runtimes to handle the sensors.

A straightforward test of this:

--- a/calvin/runtime/south/calvinsys/io/ds18b20thermometer/raspberry_pi/DS18B20.py
+++ b/calvin/runtime/south/calvinsys/io/ds18b20thermometer/raspberry_pi/DS18B20.py
@@ -28,12 +28,19 @@ class DS18B20(BaseDS18B20):
     def init(self, **kwargs):
         self._base_dir = '/sys/bus/w1/devices/'
         self._temperature = None
-        self._device_file = self._find_device_file()
+       if "index" in kwargs:
+           self._index = kwargs["index"]
+       else :
+           self._index = None
+        self._device_file = self._find_device_file(self._index)
         self._in_progress = None

-    def _find_device_file(self):
+    def _find_device_file(self, idx):
+       if idx is None :
+           idx = 0
+
         try :
-            device_folder = glob.glob(self._base_dir + '28*')[0]
+            device_folder = glob.glob(self._base_dir + '28*')[idx]
             device_file = device_folder + '/w1_slave'
         except Exception as e:
             _log.warning("Failed to find device file: {}".format(e))

And the calvin.conf entry to use the sensor on index 1 is then:

"io.temperature": {
    "module": "io.ds18b20thermometer.raspberry_pi.DS18B20",
    "attributes": {"index": 1}

@olaan
Copy link
Contributor

olaan commented Jul 2, 2018

I'm closing this issue now. If you have further questions, feel free to open a new one.

Cheers.

@olaan olaan closed this as completed Jul 2, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants