ODE to Poison Ivy

oh poison ivy!
oh poison ivy!

let me explain,
how much I’m in pain

you grow so well,
but you create a hell

your oil is attracted to my skin,
and then works to dissolve me again

oh poison ivy!
oh poison ivy!

let me explain,
you are making me insane!

Posted in Outdoors, Rant | Tagged , | Leave a comment

MQQT using Node-RED

When I started searching for how to transfer data between 2 computers using MQQT, I saw articles talking about Node-RED, but I dismissed it as being some sort of toy for children. That was probably a mistake because once I installed it, I realized it was both genius and a powerful tool. It lets you do amazing, complicated things without having to get into the weeds in every little detail.

In the screenshot below, the NR session from my Raspberry Pi is on top. The first block monitors a file to see if it changed. When it sees a change, it fires the next 2 blocks, which read the data I am looking for from a file. That data is passed on to the 3rd set of blocks (in purple) which publish the data via an MQQT broker running on the same computer. (mosquitto) The green block is just a debug block and is not needed.

The NR session on the bottom is running on my desktop. A single block (MQQT in) connects to the broker running on the Raspberry Pi and reads to values.

Here is python code running on the Raspberry Pi that retrieves the values and stores them in a pseudo-file. (the /var directory in a *nix machine is actually a location in RAM, not on the HDD or SD card)

#!/usr/bin/env python3
########################################################################
# Filename    : myProg2.py
# Description : write pi ADC values to file
# Author      : bg
# modification: 20210504
########################################################################

from time import sleep
import myADC
 
mydata = [0,0,0,0,0,0,0,0]  #create python list

def loop():
        while(True):
            for i in range(8):
                mydata[i] = myADC.getValue(i)  #bring ADC values into list
                filename = '/var/adc/'+str(i) #create the filename
                ramdisk = open(filename,'w')  #open file in writeover mode
                ramdisk.write(str(mydata[i])) #write data to file
                ramdisk.close()
            sleep(5)

if __name__ == '__main__':
    print ('Program is starting ... ')
#     print(myADC.getValue(0))
#     print(myADC.getValue(1))
    try:
        loop()
    except KeyboardInterrupt:
        #destroy()
        pass
Posted in Industrial Automation | Tagged , , , | Leave a comment

Raspberry Pi Data to Google Sheets

A Raspberry Pi 3B is configured to bring in sensor data through a ADC (analog to digital converter) connected to the pi’s I2C (Inter-Integrated Circuit) serial interface. (I’m using the ADS7830 module that came with a kit that contained all kinds of fun project goodies). I have 2 potentiometers wired to the ADC that I can turn by hand to give me 2 real voltage values. The pi has internet access through wifi.

Raspberry Pi 3B connected to a prototyping breadboard with the ADS7830 module and 2 potentiometers. A backlight LCD is also shown, but not part of this project.

ADC to Pi

I followed the kit‘s instructions for the python language to

  • enable the I2C interface in the Pi’s configuration
  • install the needed os packages
  • import the needed python modules
  • wire the ADC module to the Pi
  • wire the pots to the ADC

I wrote a function so I can get the ADC values from other programs:

#!/usr/bin/env python3
########################################################################
# Filename    : myADC.py
# Description : Use ADC module to read the voltage values
# Author      : sparkygeek.com
# modification: 2020/04/26
########################################################################
from ADCDevice import *
adc = ADS7830()
    
def getValue(chan = 0):
    value = adc.analogRead(chan)    # read the ADC value of channel 0
    voltage = value / 255.0 * 3.3  # calculate the voltage value
    print ('Channel %d ADC Value : %d, Voltage : %.2f'%(chan,value,voltage))
    return voltage

Pi to Google

Follow the directions for getting the Google Python Quickstart project working. (Enable Google Cloud Platform Project API, Create credentials, download your credential file, and install the needed Google client libraries) The quickstart code uses the spreadsheets.values.get method, but we are going to use the spreadsheets.values.append method to add new values to our spreadsheet. Here is the code I’m using to do this:

from __future__ import print_function
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
import myADC
from time import sleep

# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']

# The ID and range of a sample spreadsheet.
SPREADSHEET_ID = 'xxxxxxxxxxRb6BXNBORw72feWN3iiaz_xxxxxx'
RANGE_NAME = 'Sheet1'

def main():
    """Shows basic usage of the Sheets API.
    Prints values from a sample spreadsheet.
    """
    creds = None
    # The file token.json stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.json', 'w') as token:
            token.write(creds.to_json())

    service = build('sheets', 'v4', credentials=creds)

    # Call the Sheets API
    sheet = service.spreadsheets()

    newData = [myADC.getValue(0),myADC.getValue(1)]  # get data from ADC module

    body = {
            "values": [
                newData
            ]
           }
    result = sheet.values().append(
           spreadsheetId=SPREADSHEET_ID, range=RANGE_NAME,
           valueInputOption='USER_ENTERED', body=body).execute()
    print('{0} cells appended.'.format(result \
                                          .get('updates') \
                                          .get('updatedCells')))

if __name__ == '__main__':
    while(True):
        main()
        sleep(2.5)

See it in action!

Posted in Industrial Automation, Projects | Tagged , , , , | Leave a comment

Ignition User Pop Up Data Entry Box

If you want a data-entry pop-up in Inductive Automation’s Ignition, you just call the function to open an input box. This seems simple to computer programmers, but it is different than how typical industrial automation systems accomplish the task. Ignition marries the two worlds, which is one of the things that makes it awesome.

the blue button at the top of the screen has a script that executes when pressed
the script calls the input box, converts to a float, then stores the value in a system tag
standard input box with the parameters from the script
Posted in Industrial Automation | Tagged , , | 2 Comments

My Political Opinions Lean…

My political opinions lean more and more to Anarchy… The most improper job of any man, even saints, is bossing other men. Not one in a million is fit for it, and least of all those who seek the opportunity. -JRR Tolkien

Posted in Politics | Tagged , , | Leave a comment

Shop Construction Photos

Posted in Projects | Tagged , , | Leave a comment

Bathroom Remodel Pictures

Posted in Projects | Leave a comment

Getting Ambient Weather Data into Ignition

I wanted to learn how API’s work, so I figured bringing in some of my weather station data to an Inductive Automation’s Ignition project would be a good task to learn on. I wasted a lot of time trying to use the python helper functions that were recommended in the docs at https://ambientweather.docs.apiary.io/. The problem with that route was that Ignition uses it’s own instance of python (actually jython)and it’s an older version. Note: if you do need to import libraries, I finally learned you can use the pip install command which takes care of all the dependencies for you if you follow the directions at https://forum.inductiveautomation.com/t/procedure-for-installing-python-libraries-from-source/26022/2. I almost had it working, but ran into a problem with a codec, which I didn’t understand and didn’t want to figure out.

I wound up using Ignition’s system.net.httpClient() function to make the call to the API. Once I figured out how to format the API call, the hard part was figuring out how to pick through the JSON document it returned and update tag values within Ignition with those values. I couldn’t get any of the built-in functions that are supposed to help with this to work correctly. The documentation is pretty sparse on those functions and it seemed to get hung up on the fact the date values contained colons. My workaround was to convert it to a big string value, then use python’s string commands to find the parts I wanted.

Below is the script I use to do the work. The script runs every 5 seconds. One of the software developers at Ignition had a much more elegant script started, but the get_data() method had a problem and I just went with a simpler solution.

#get data from ambient weather API
client = system.net.httpClient()
response = client.get("https://api.ambientweather.net/v1/devices/00:0E:00:00:00:00", params={"apiKey":"xxx","applicationKey":"yyy","limit":"1" }) 
#comes in as a list
weatherdata = response.json
#convert to a string to deal with problem of time having colons
weatherdataString = ' '.join(map(str, weatherdata)) 

#change searchterm for each element you want
searchterm = "tempf"
offset = len(searchterm)+3
startpoint = weatherdataString.find(searchterm)
endpoint = weatherdataString.find("'",startpoint+offset)
system.tag.write("[default]wxstation/temperature",(weatherdataString[startpoint+offset:endpoint-2]))

#change searchterm for each element you want
searchterm = "yearlyrainin"
offset = len(searchterm)+3
startpoint = weatherdataString.find(searchterm)
endpoint = weatherdataString.find("'",startpoint+offset)
system.tag.write("[default]wxstation/yearlyrain",(weatherdataString[startpoint+offset:endpoint-2]))

#change searchterm for each element you want
searchterm = "dailyrainin"
offset = len(searchterm)+3
startpoint = weatherdataString.find(searchterm)
endpoint = weatherdataString.find("'",startpoint+offset)
system.tag.write("[default]wxstation/dailyrain",(weatherdataString[startpoint+offset:endpoint-2]))

Posted in Industrial Automation | Tagged , , | Leave a comment

Loft Ladder

I built a ladder out of 2×4 and 1×4 lumber. It has 9 rungs spaced 12-3/4″ apart and is set to be used at a 70ยบ angle and have 9′-1″ of elevation.

Posted in Projects | Tagged , | Leave a comment

How-To Get Clean Dishes with a Modern Dishwasher

If your modern high-efficiency* dishwasher is anything like mine, then it is a source of constant frustration. Even with pre-washing the dishes don’t come out very clean.  I think I solved the problem, here is what we do:

  • wash out the filter every time a load is run
  • run it with soap for a normal cycle, which takes several hours (can cancel it during the dry cycle if caught in time) (don’t use too much soap — filling the cups full is usually not necessary)
  • pour a gallon of clean water into the base, then press the drain/cancel button
  • repeat with another galon of clean water, then draining
  • run it again but without soap on the 1-hour cycle

The extra steps are necessary to wash away all the soap.  There are 2 things you are fighting**: 1) the soaps no longer have the ingredient in them that allow for easy rinsing, and 2) a typical cycle only uses 3 gallons of water, instead of the traditional 15 gallons.

*water usage efficiency, not time efficiency
**both of the changes are due to US law

 

 

Posted in How-To, Information | Tagged , , , | Leave a comment