EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  <2024 Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  <2024
<== Date ==> <== Thread ==>

Subject: Re: [Ext] Re: Room temperature and humidity monitoring with EPICS?
From: Jesse Hopkins via Tech-talk <tech-talk at aps.anl.gov>
To: Matt Newville <newville at cars.uchicago.edu>, EPICS Tech Talk <tech-talk at aps.anl.gov>
Date: Tue, 26 Mar 2024 14:20:50 -0500
Hi Matt (and anyone else interested),

We did end up getting the EM32 sensors (we got the EM32-Xe, mostly for PoE support, but I think the Le would have worked just as well in retrospect and been a bit cheaper). Implementing the EPICS monitoring has been a low priority project for me, but I finally spent a bit of time on it. I took the script you sent and adapted it so that it's using pythonSoftIOC to create the records and IOC that holds the device data, mostly because I'm much more familiar with python than EPICS. That means that everything including the IOC creation is done in a single python script. In case you're interested, I've attached the script.

I did appreciate the example script, I used your esensor class, lightly modified, and it definitely saved me a lot of time in parsing the json/xml!

All the best.

- Jesse

----
Jesse Hopkins, PhD
Deputy Director
BioCAT, Sector 18
Advanced Photon Source


On Mon, Nov 13, 2023 at 12:11 PM Matt Newville <newville at cars.uchicago.edu> wrote:
Hi Jesse, 


Great - yeah, let me know if you have any questions.

On Mon, Nov 13, 2023 at 11:17 AM Jesse Hopkins <jhopkins1 at iit.edu> wrote:
Hi Matt,

Thanks! Looks pretty straightforward, but if I have questions I may ping you once I have the sensors.

All the best.

- Jesse

----
Jesse Hopkins, PhD
Deputy Director
BioCAT, Sector 18
Advanced Photon Source


On Mon, Nov 13, 2023 at 9:53 AM Matt Newville <newville at cars.uchicago.edu> wrote:
Hi Jesse, 

Sorry for the delay.   What I do is fairly simple.  For each sensor, I load a small record into a softIOC with
        dbLoadRecords("esensor.db","P=13IDE:,Q=Esensor")

in the startup script and with the "esensor.db" of (also attached):

####
# record used for Esensor temperature, humidity sensors
record(ai,"$(P)$(Q):TempF") {
           field(DESC, "Temperature (F)")
           field(VAL,  "0")
}

record(ai,"$(P)$(Q):TempC") {
        field(DESC, "Temperature (C)")
        field(VAL,  "0")
}

record(ai,"$(P)$(Q):Humid") {
          field(DESC, "Humidity (percent)")
          field(VAL,  "0")
}

record(stringin, "$(P)$(Q):TimeStamp") {
           field(DESC, "Timestamp of last update")
           field(VAL,  "")
}
####

Then, I run a Python script (attached) to read the sensor (over plain HTTP), parse the results, and push to PVs.  The script handles both older Esensors that send data as XML, and newer ones that send JSON data.  You could probably put that script inside procServ or something like that but I just have it running as a simple background script.  

--Matt


On Fri, Nov 10, 2023 at 4:15 PM Jesse Hopkins <jhopkins1 at iit.edu> wrote:
Hi folks,

Thanks to everyone who shared what you're using! I think what Matt or Pierrick suggested are right in line with what I had in mind.

The price point on something with temperature and humidity is a bit better from the Esensors that Matt shared, so that's where I'm leaning. Matt, if you'd be willing to share your python script that would be great. I was looking at the EM32-Xe, because the display would be a nice feature to have. Have you used that one?

All the best.

- Jesse

----
Jesse Hopkins, PhD
Deputy Director
BioCAT, Sector 18
Advanced Photon Source


On Fri, Nov 10, 2023 at 12:37 PM Matt Newville <newville at cars.uchicago.edu> wrote:
Hi Jesse, 

We have a few of the Esensors temperature/humidity sensors in our hutches, including the EM32-Le  https://eesensors.com/products/websensors/environmental-monitor-sensor.html 

These have a simple magnetic mount and an ethernet port.  They provide a simple web app to read the temperature and humidity. But, there is also an HTTP API to get XML or JSON (depending on the generation of the sensor) with the data. Fetching and reading those values and putting them into Epics PVs is pretty straightforward.  I'd be happy to share the Python script we use to read multiple meters and put them into PVs so we can monitor temperature and humidity and put those values into metadata.

--Matt


On Fri, Nov 10, 2023 at 11:47 AM Jesse Hopkins via Tech-talk <tech-talk at aps.anl.gov> wrote:
Hi folks,

We're looking for a way to monitor humidity and temperature in our experimental enclosures (beamline hutches), with readings that we can access via EPICS. I'd prefer something ethernet based, and low cost is always good. I strongly suspect folks in the EPICS community have already done this in a variety of ways, though I wasn't able to turn anything obvious up in a search. 

Does anyone have a good solution that you're using?

I suspect that if there's nothing else out there I could just buy some analog input boxes (e.g. LabJack or Measurement Computing) and hook up a couple of sensors, but I'm hoping there's a better/cheaper solution.

All the best.

- Jesse

----
Jesse Hopkins, PhD
Deputy Director
BioCAT, Sector 18
Advanced Photon Source


--
--Matt Newville <newville at cars.uchicago.edu> 630-327-7411


--
--Matt Newville <newville at cars.uchicago.edu> 630-327-7411


--
--Matt Newville <newville at cars.uchicago.edu> 630-327-7411
import asyncio
import traceback
import time
import json
import xml.etree.ElementTree as ET

from softioc import softioc, builder, asyncio_dispatcher
# import cothread
import requests

class ESensor:
    """ read Esensor data, push to Epics PVs"""
    def __init__(self, label, url, tempc_pv, tempf_pv, humid_pv, timestamp_pv):
        self.label = label
        self.url = url
        # self.prefix = prefix
        self.format = url.split('.')[-1]

        self.tempf_pv = tempf_pv
        self.tempc_pv = tempc_pv
        self.humid_pv = humid_pv
        self.timestamp_pv = timestamp_pv

        if self.format == 'xml':
            self.tattr = "tm0"
            self.tunit = "tun0"
            self.hattr = "hu0"
            self.reader = ET.fromstring
        elif self.format == "json":
            self.tattr = "tmp"
            self.hattr = "hum"
            self.tunit = "tun"
            self.reader = json.loads

    def getval(self, source, attr, force_float=True):
        if self.format == "xml":
            try:
                val = source.find(attr).text
            except:
                val = "-1"
        elif self.format == "json":
            try:
                val = source.get(attr, -1.0)
            except:
                val = "-1"
        if force_float:
            try:
                val = float(val)
            except:
                val = -1.0
        return val

    def read(self):
        resp = requests.get(self.url, timeout=0.5)
        tdat = self.reader(resp.content.decode("utf-8"))

        tunt = self.getval(tdat, self.tunit, force_float=False)
        tval = self.getval(tdat, self.tattr)
        hval = self.getval(tdat, self.hattr)
        return tval, tunt, hval

    def update(self):
        tval, tunit, hval = self.read()
        if tunit in (0, '0', 'F'):
            tval_f = tval
            tval_c = (tval - 32)*5.0/9.0
        else:
            tval_c = tval
            tval_f = (tval_c * 9.0/5) + 32
        tval_f = float("%.3f" % tval_f)
        tval_c = float("%.3f" % tval_c)
        hval   = float("%.3f" % hval)

        self.tempc_pv.set(tval_c)
        self.tempf_pv.set(tval_f)
        self.humid_pv.set(hval)
        self.timestamp_pv.set(time.ctime())

if __name__ == '__main__':
    # Create an asyncio dispatcher, the event loop is now running
    dispatcher = asyncio_dispatcher.AsyncioDispatcher()

    esensors = []

    # Define as prefix, url
    sensor_defs = [
        ['18ID:EnvMon:D', "http://164.54.204.197/status.json";],
        ['18ID:EnvMon:C', "http://164.54.204.198/status.json";],
        ['18ID:EnvMon:A', "http://164.54.204.199/status.json";],
        ]

    for sensor_def in sensor_defs:
        prefix = sensor_def[0]
        url = sensor_def[1]
        # Set the record prefix
        builder.SetDeviceName(prefix)

        # Create some records
        ai_c = builder.aIn('TempC', initial_value=0, DESC='Temperature (C)')
        ai_f = builder.aIn('TempF', initial_value=0, DESC='Temperature (F)')
        ai_h = builder.aIn('Humid', initial_value=0, DESC='Humidity (percent)')
        si_t = builder.stringIn('TimeStamp', initial_value="", DESC='Timestamp of last update')

        esensor = ESensor(prefix, url, ai_c, ai_f, ai_h, si_t)
        esensors.append(esensor)

    # Boilerplate get the IOC started
    builder.LoadDatabase()
    softioc.iocInit(dispatcher)

    # Start processes required to be run after iocInit
    async def update():
        while True:
            try:
                [e.update() for e in esensors]
            except Exception:
                # traceback.print_exc()
                pass
            await asyncio.sleep(0.5)

    dispatcher(update)

    # Finally leave the IOC running with an interactive shell.
    softioc.interactive_ioc(globals())

Navigate by Date:
Prev: Re: How can I get a PV from an external server from inside a docker container? Florian Feldbauer via Tech-talk
Next: Re: urldefense.us URLs? (was: New Git mirror for the SNL-Sequencer (code and docs)) J. Lewis Muir via Tech-talk
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  <2024
Navigate by Thread:
Prev: Re: How can I get a PV from an external server from inside a docker container? Carsten Winkler via Tech-talk
Next: CS-Studio Phoebus: Customizing a Scaled Slider (Pre-Multiplier, Logarithmic?) Feister, Scott via Tech-talk
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  <2024
ANJ, 27 Mar 2024 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·