My first python app, controling arduino


#1

Hello!

I just finished Learn Python The Hard Way book. The book was great for beginner like myself. The last few excercises were difficult, and I am still struggling with object-oriented programming and nostes. I need to study more.

While I was studying the book, I was doing this arduino project to water my plants over the internet when away from home and collect data like temperature, amount of light and moisture of the soil. For that I did a simple python script. This works well when arduino is connected to USB port.

So, my next step is to use my old laptop or Raspberry Pi as a server and control the arduino with web browser. All the feedback are welcome, and tips to get this running online.

This is how the code looks like now:

import serial #serial
import time #time
import os
import datetime #date and time
import csv #imports csv format
from sys import exit

ser = serial.Serial('COM5', 9600)

def cls():
    os.system ("cls")

def main_menu(): # main menu
    cls()
    print("Choose:")
    print("1: Add water (l).")
    print("2: Get current data.")
    print("3: Data logging menu.")
    print("4: Quit")

def data_log_menu(): # Data logging menu
    print("Choose data logging intervals:")
    interval = int(input(">"))
    print(f"{interval} seconds, write \"esc\" to RETURN main menu.")
    tCurrent = time.time()

    while True: # loops until the end
        print(time.time())

        if time.time() >= tCurrent + interval: # starts the data_log every given interval, example 300 seconds
            ser.flushInput()
            time.sleep(2)
            data_log()
            tCurrent = time.time()

def water(): #tells the arduino to add water, then asks how much
    time.sleep(3)
    add_water = ("add water")
    ser.write(add_water.encode())
    time.sleep(3)
    print("How much water?")
    amount = input(">")
    amount_i = int(amount)
    print(f"{amount}, OK? Press RETURN to continue.")
    ok = input(">")
    ser.write(amount.encode())
    water_add = (amount_i/0.11)
    print("Adding water...")
    time.sleep(water_add)
    cls()
    True

def get_data():
    cls()
    time.sleep(5)
    valo = ("read_light")
    ser.write(valo.encode()) #asks arduino the needed info, value of the light sensor
    time.sleep(6)
    print("Light: ", end = '')
    read_light = ser.read(ser.inWaiting()).decode('utf-8')
    print(read_light)
    ser.flushOutput()
#-------------------------------------------------- value of the soil moisture
    time.sleep(5)
    kosteus = ("read_moisture")
    ser.write(kosteus.encode())
    time.sleep(6)
    print("Moisture: ", end = '')
    read_moisture = ser.read(ser.inWaiting()).decode('utf-8')
    print(read_moisture)
    ser.flushOutput()
#---------------------------------------------------- volume of the water container
    time.sleep(5)
    tilavuus = ("read_volume")
    ser.write(tilavuus.encode())
    time.sleep(6)
    print("Volume of the water: ", end = '')
    read_volume = ser.read(ser.inWaiting()).decode('utf-8')
    print(read_volume)
    ser.flushOutput()
#------------------------------------------------------ #value of the temperature sensor

    time.sleep(5)
    lampo = ("read_temperature")
    ser.write(lampo.encode())
    time.sleep(6)
    print("Temperature: ", end = '')
    read_temperature = ser.read(ser.inWaiting()).decode('utf-8')
    temp = float(read_temperature)
    print(temp)
    time.sleep(5)
    ser.flushOutput()
    back = input("Press RETURN to main menu. \nWrite: \"load\" to load again.\n")
    if back == "load":
        get_data()
    True

def data_log():
#---------------------------------------asks arduino the needed info, value of the light sensor
    time.sleep(5)
    valo = ("read_light")
    ser.write(valo.encode())
    time.sleep(6)
    print("Light>>>>")
    read_light = ser.read(ser.inWaiting()).decode('utf-8')
    print(read_light)
    ser.flushOutput()
#----------------------------------------value of the soil moisture
    time.sleep(5)
    kosteus = ("read_moisture")
    ser.write(kosteus.encode())
    time.sleep(6)
    print("Moisture>>>>")
    read_moisture = ser.read(ser.inWaiting()).decode('utf-8')
    print(read_moisture)
    ser.flushOutput()
#----------------------------------------volume of the water container
    time.sleep(5)
    tilavuus = ("read_volume")
    ser.write(tilavuus.encode())
    time.sleep(6)
    print("Water>>>>")
    read_volume = ser.read(ser.inWaiting()).decode('utf-8')
    print(read_volume)
    ser.flushOutput()
#-----------------------------------------#value of the temperature sensor
    time.sleep(5)
    lampo = ("read_temperature")
    ser.write(lampo.encode())
    time.sleep(6)
    print("Temperature>>>>")
    read_temperature = ser.read(ser.inWaiting()).decode('utf-8')
    temp = float(read_temperature)
    print(temp)
    time.sleep(5)
    ser.flushOutput()
#-------------------------------------------------------

    log_file = open('log_1.csv', 'a', newline='') # opens csv file
    with log_file: # writes the info to csv file
        loki_aika_nyt = datetime.datetime.now()
        loki_aika = loki_aika_nyt.strftime('%Y/%m/%d %H:%M:%S')
        myData =[(loki_aika), (read_light), (read_moisture), (read_volume), (temp)]
        writer = csv.writer(log_file, dialect='excel')
        writer.writerow(myData)

while True: # starting main loop, infinite

    main_menu()
    choice = input(">")
    if choice == "1":
        water()
    elif choice == "2":
        get_data()
    elif choice == "3":
        data_log_menu()
    elif choice == "4":
        quit()
    else:
        print("Choose a number!")

And the logfile looks, like this:

loki_aika, read_light, read_moisture, read_volume, read_temperature
2018/08/12 21:21:29,385,0,13.55,28.12
2018/08/12 21:22:30,382,0,13.56,28.12
2018/08/12 21:23:31,379,0,13.58,28.12
2018/08/12 21:24:32,371,0,13.29,28.19

#2

Hey that’s pretty neat and I love the idea of the software!

You could add some error handling in case the inputs do not match your expectations (and cannot be converted to an integer).

This would be a great candidate for Zed’s Refactor School.


#3

Hey that’s a good start. You might want to check out the OOP School content for more on objects and classes:

Now, in your code I think what you should do is take all the parts where you have blocks of code like:

#------------------------------------------

And put each of those into a function, then call those functions. Do it slowly, pulling one block into a function, then run it all and make it work, then do the next block, run, next block, run, etc. This will probably open you up to new ideas and you’ll start to see repetition and go from building a script to building a system you can use.


#4

Thank you for the tips! I definetly have to add some error handling and make functions out of these blocks. I just realised that why didn’t I do it earlier.

OOP schools looks like just what I needed, thanks!


#5

I think Im starting to get OOP slowly.

So when I started to make function, I realised that I have to make classes. I got like 50 lines away from the code. Had to look back the LPTHW book couple of times, but this looks much nicer now.

class Sensors:

    def __init__(self, sensor):
        self.sensor = sensor

    def readsens(self): #method for asking arduino the reading from the sensors
        time.sleep(5)
        command = (self.sensor)
        ser.write(command.encode()) #asks arduino the needed info.
        time.sleep(6)
        result = ser.read(ser.inWaiting()).decode('utf-8')  # gives the result from the sensor
        return result
        ser.flushOutput()

senslight = Sensors('read_light')
sensmoisture = Sensors('read_moisture')
sensvolume = Sensors('read_volume')
senstemp = Sensors('read_temperature')

#6

Yeah, that’s good so far. Check out some new supplemental material I did in my LCTHW:Live classes:

https://tv.learncodethehardway.com/videos/search?search=OOPSchool

The code for that series is:

Go through those and you’ll build your own OOP. I’d say start with Seminar 5, then to 1-5 again. That’ll give you a good idea of what it’s doing.