Ad

Comparing Data From Sensors In Arrays Before Writing A Csv File Doesn't Work, Raspberry Pi

- 1 answer

I'm writing a datalogging program for my Raspberry Pi which is going pretty well. But before writing a new line in a csv file i want to check if the data is different to make sure i don't write the same data twice. The problem is, i use an array to store the new data, and an array to store the old data. when they are different the code will write a new line in the CSV file and make the old and the new data the same, then new data will change again. But for some reason the old != new is not working in finding if the data is changed.

I tried using more global variables and no local variables. I tried using the Values Array (all sensors data, also a lot of 0) in stead of the Info Array (active sensor data). I thought it might be a pointer issue but i don't think Python uses pointers. When i write the line "old = new" in the if new != old: loop. (see code) it only goes trough once and stops. The thing that really bums me out is that when i change the sensor values and i use CTRL + C to interrups. it seems like old and new are ALWAYS the same. but they have updated to the new values. I tried to debugg the code but the Pi and Thonny both freeze when i get to the libraries.

######################Variables#######################
AnalogSensor = [False, True, False, False, False, True, True, True]    #8 analog inputs
AnalogSensorDelay = [1, 1, 1, 1, 1, 0.4, 2, 0.8]                       #the timing to read all of these inputs.
LoggingDelay = 1                                                       #the timing to write the Csv file
# DHT = 17 #not yet connected
Decimalen = 0                                                          #the amount of decimals after the decimal dot (1 means only logging 100th of seconds, 2 means 10th of a second. etc.)

###############Libraries##################
import time
import csv
from datetime import date
import datetime
import os.path
import Adafruit_GPIO.SPI as SPI
import Adafruit_MCP3008

###############Initial values of variables#############

name = "/media/pi/DIRK\040PI/" + str(date.today()) + ".0.csv"          #this will write a file to this path (USB stick in the pi) with the name 2019-09-25.0.csv
old = 1
new = 0
SPI_PORT   = 0
SPI_DEVICE = 0
starttime = time.time()
Sensortimer = [0]*8
values = [0]*8
Loggingtimer = 0
dataArray = [0]

###############Setup###################################

if Decimalen == 0:
    Decimalen = -1                                                    #gets rid of the decimal dot of there are no decimals.
mcp = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE))
data = [0]*(AnalogSensor.count(True))                                 #creates an array with the size of the amount of inputs put on True

def Filename(name):                                                   #this function changes the '2019-09-25.0.csv' name and changes the .0 to .1 .2 etc if the file already exists.
    x = 0
    while os.path.isfile(name):                                       #checks if the file exists.
        print("There was already a file named",name)
        x = x + 1                                                     #increase file number
        name = "/media/pi/DIRK\040PI/" + str(date.today()) + "." + str(x) + ".csv" 
    print("Creating file ", name)
    return name

def Writecsv(name, delay, info):                                      #writes a csv file with the name returned by the Filename function, with the delay chosen with LoggingDelay, and info is an array filled with active sensor data.
    global Loggingtimer
    global totaltime
    global starttime
    global old
    global new
    if (Loggingtimer + delay <= totaltime - starttime):              #checks if delay time has passed ( this is to bypass the sleep function and to be able to do other things )
        Loggingtimer = time.time() - starttime
        new = info                                                   #changes new to an array with the same values as info ( an array with active sensor values )
        if new != old:                                               # if new data is different from old data
            print(info)                                              # prints data in the Shell for comfort.
            write = str(datetime.datetime.now())                     ##
            write = write.split()                                    ##
            write[1] = write[1][:(9+Decimalen)]                      ##
            data = [write[0],write[1]]                               ## Creates a timestamp with 2 values, the date, and the time with chosen decimals
            for x in range((AnalogSensor.count(True))):              ## Add the data from the info array to the timestamp array to create the LOG.
                data.append(info[x])                                 ##
            with open(name,'a',newline="") as f: 
                writer = csv.writer(f)
                writer.writerow(data)                                # Write a row with the complete log
            print("Writing to file")
----------------------------------------------------------------------
#            old = new
----------------------------------------------------------------------            

def Analogread(pin, delay):                                          # This function reads the values of the MCP3008 and puts them in array Values, note. the array Values has the values of all 8 inputs, even if some inputs are disabled
    global totaltime
    global Sensortimer
    global starttime
    if (Sensortimer[pin] + delay <= totaltime - starttime):
        Sensortimer[pin] = time.time() - starttime
        values[pin] = mcp.read_adc(pin)
        return values[pin]

name = Filename(name)
while True:
    totaltime = time.time()                                          # Keeps updating the time
    y = 0
    for counter in range(8):                                         # Looks in the AnalogSensor array how many sensors are on True
        if AnalogSensor[counter] == True:
            Analogread(counter, AnalogSensorDelay[counter])          # Read the value of the inputs that are on True, ( the inputs on false will always be 0 )
            data[y] = values[counter]                                # Puts the data from the active sensors in a different array with only active sensor data
            y = y + 1
    Writecsv(name, LoggingDelay, data)                               

i expect the output to be someting like:

There was already a file named /media/pi/DIRK PI/2019-09-25.0.csv
There was already a file named /media/pi/DIRK PI/2019-09-25.1.csv
There was already a file named /media/pi/DIRK PI/2019-09-25.2.csv
Creating file /media/pi/DIRK PI/2019-09-25.3.csv
[7, 0, 700, 254]
Writing to file
[4, 0, 702, 254]
Writing to file
[9, 0, 697, 356]
Writing to file
[3, 0, 707, 456]
Writing to file
[2, 0, 712, 677]
Writing to file

But after

[7, 0, 700, 254]
Writing to file

It just stops. No error. And keeps updating both Old and New data arrays which stay the same for some reason. Even though old can only be updated to the value of new after old and new are different and a new line is written in the csv file.

Ad

Answer

So i am not completely sure where the problem was in the code above, but after getting a lot more experience with python I started working with classes. This is the second version of my datalogger code and this works perfectly :D.

import csv
from os.path import exists
import datetime
import RPi.GPIO as GPIO
from time import sleep
from random import randrange
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)


class Logger:
    path = '/home/pi/Documents/Namelogs/'
    date = str(datetime.date.today())
    fieldnames = ['date','time']
    def __init__(self, name = 'datalog.0.csv'):
       '''
       Omschrijving
        -------------------
            Deze class beschikt over alle functies die te maken hebben met het loggen van data. 

        Variabelen
        ----------
        path: 'string'
            Het pad waar de csv file opgeslagen gaat worden.
        date: 'string'
            De datum wanneer het programma begint.
        fieldnames: [list]
            De extra waarden die in de datalog moeten staan(behalve de data), default = datum en tijd.
        data: {dictionary}
            Hierin staat alles wat gelogd moet worden, het is een dictonary opgebouwd uit 4 keys:
                -Data : hierin komt alle nieuwe data te staan
                -Old : hierin komt alle oude data te staan
                -Date : hierin komt de huidige datum te staan
                -Time : hierin komt de huidige tijd te staan

        Parameters
        ----------
        name : `string`
            De naam van het bestand dat je wilt loggen, de standaard naam is datalog

        Alle andere helper functies:
            - log_data()
            - collect_data()
            - check_data()
            - print_data()
            - check_file()
            - check_date()
        '''
        self.data = {}
        self.name = name
        self.data['data'] = 0

        self.fileName = str(self.path + self.name + str(datetime.date.today()) + ".0.csv")


    def collect_data(self):
        '''
        Deze functie verzamelt de data die gelogd moet worden, de data wordt opgeslagen onder "data"
        wanneer nieuwe data gelogd wordt worden de waardes uit "data" verplaatst naar "old"

        De oude data kan gebruikt worden door de volgende functie:
            - check_data()

        '''
        self.data['old'] = self.data['data']
        data_array = {}
        for sensor in Sensor.sensorArray:
            if sensor.state:
                data_array[sensor.name] = sensor.read_data()
        self.data['data'] = data_array

    def log_data(self):
        '''
        Korte Omschrijving
        ------------------
            Deze functie logt de huidige data uit de data dictionary in een csv bestand onder de naam gespecificeerd bij de class.

        Uitgebreide Omschrijving
        ------------------------
            Deze functie haalt de huidige datum en tijd op, en combineert dit met de data uit de data dictionary.
            Deze combineert hij in een nieuwe dictionary en hij schrijft deze weg onder de juiste headers.
            Deze headers zijn aangemaakt bij de create_file() functie.
        '''
        self.data['date'] = str(datetime.datetime.now().date())
        self.data['time'] = str(datetime.datetime.now().time())
        with open(self.fileName,'a',newline="") as f:
            writer = csv.DictWriter(f, fieldnames = Logger.fieldnames)
            writer.writerow({'date' :self.data['date'], 'time' : self.data['time'][:12], **self.data['data']})

    def check_data(self, compare_data = True):
        '''
        Omschrijving
        ------------
            Deze functie vergelijkt de data uit collect_data() met de oude data uit collect_data() en geeft op basis daarvan een waarde terug, zie Return.
            Deze functie kan gecombineerd worden met de log_data() functie zodat alleen verschillende data gelogd wordt.

        Voorbeeld
        ---------
            if logger.check_data():
                logger.log_data()

        Parameters
        ----------
        compare_data : Boolean (True of False, standaard = True)
            Deze variabele bepaald of deze functie wel of niet uitgevoerd wordt.

        Return
        ------
        True / False: Boolean
            Deze functie geeft False terug wanneer de data het zelfde is als de oude data.
            In alle andere gevallen geeft deze functie True terug.
        '''
        if compare_data:
            if self.data['data'] == self.data['old']:
                print("Data not logged, since data == old data")
                return False
            else:
                return True        
        return True

    def print_data(self):
        '''
        Omschrijving
        ------------
            Deze functie print de data die verzamelt is met de collect_data() functie op een nette en leesbare manier in de shell.
        '''
        print("-"*150)
        print(self.data['data'])
        print("-"*150)

    def create_file(self):
        '''
        Korte omschrijving
        -------------------
            Deze functie maakt het bestand met de gekoze naam aan en schrijft een header.

            Belangrijk! - Dit is altijd de eerste functie die aangeroepen wordt.

        Uitgebreide omschrijfing
        -------------------------
            Deze functie kijkt of er al een bestand is met de gekozen naam. ("gekozen naam"_"datum".0.csv)
            Als dit bestand al bestaat gaat de functie het getal in de naam aanpassen totdat er een bestandsnaam gevonden is die wel beschikbaar is.
            Zodra deze gevonden is gaat de functie een header schrijver met de datum, de tijd en alle actieve sensoren.

        Return
        -------
        self.fileName : 'Sting'
            De complete naam van het log bestand 
            ("gekozen naam"_"datum".x.csv)
        '''
        x = 0
        self.fileName = self.path + self.name + "_{}.{}.csv".format(str(datetime.date.today()),x)
        while exists(self.fileName):
            x+=1
            self.fileName = self.path + self.name + "_{}.{}.csv".format(str(datetime.date.today()),x)
        with open(self.fileName,'a',newline="") as f:
            writer = csv.DictWriter(f, fieldnames = Logger.fieldnames)
            writer.writeheader()
        return self.fileName

    def check_date(self):
        '''
        Korte omschrijving
        -------------------
            Deze functie kijkt of het een nieuwe dag is, zo ja maakt hij een nieuw bestand.

        Uitgebreide omschrijfing
        -------------------------
            Deze functie vergelijkt de datum wanneer het laatste bestand gemaakt is met de huidige datum,
            als deze niet het zelfde zijn wordt de create_file() functie aangeroepen die een nieuw datalog bestand maakt, zie create_file().
            waarna de datum wanneer het laatste bestand gemaakt is veranderdt wordt naar vandaag.
        '''
        dateNow = str(datetime.date.today())
        if dateNow != self.date:
            self.date = dateNow
            self.create_file()

class Sensor(Logger):
    sensorArray = []
    def __init__(self, name, state, pin):
        self.name = name
        self.state = state
        self.pin = pin

        GPIO.setup(pin, GPIO.OUT)   
        GPIO.output(pin, state)
        if self.state:
            Logger.fieldnames.append(self.name)
            Sensor.sensorArray.append(self)

    def read_data(self):
        if self.name == 'digital sensor 1':
            data = randrange(2)
        else:
            data = randrange(3)*randrange(15)
        return data


#while True:
#    logger.collect_data()
#    logger.check_date()
#    logger.print_data()
#    if logger.check_data():
#        logger.log_data()
#    sleep(0.2)
Ad
source: stackoverflow.com
Ad