Big rewrite !!!

Data objects has subclasses for GPS, system and BME280 and moved to 
module
This commit is contained in:
Pierrick C 2018-09-09 11:39:21 +02:00
parent 9b36c49893
commit fba971349c
2 changed files with 455 additions and 328 deletions

View File

@ -0,0 +1,421 @@
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Cameteo module
Data classes for GPS, BME280 sensors and some system stuff
Function to handle data files rotation and set internal RTC date from other
(more reliable) sources.
"""
import os
import time
import rtc
import microcontroller
import board
from busio import I2C, UART
from analogio import AnalogIn
from digitalio import DigitalInOut, Direction
from adafruit_bme280 import Adafruit_BME280_I2C
from adafruit_gps import GPS
TIME_FORMAT = "{:04}/{:02}/{:02}_{:02}:{:02}:{:02}" # Date/time format
# Set the pin N°13 to use the onboard LED as read-only/read-write indicator
# (RED = read-only, no data recorded)
LED13 = DigitalInOut(board.D13)
LED13.direction = Direction.OUTPUT
LED13.value = False
###########
# Classes #
###########
class Data:
"""Class for handling data"""
def __init__(self,
name,
update_interval=10,
write_interval=300,
send_interval=60,
debug=False):
self.name = name
self.data = {}
self.debug = debug
self.update_interval = update_interval
self.write_interval = write_interval
self.send_interval = send_interval
self._last_update = 0
self._last_backup = 0
self._last_send = 0
# Check if data directories exists if we need to use them
if self.write_interval >= 0:
try:
if 'data' not in os.listdir():
os.mkdir('data')
os.mkdir('data/hourly')
os.mkdir('data/daily')
elif 'hourly' not in os.listdir('data'):
os.mkdir('data/hourly')
elif 'daily' not in os.listdir('data'):
os.mkdir('data/daily')
except OSError as err:
print("Err {}: readonly".format(err))
self.write_interval = -1 # to avoid trying again till next reset
# Turn onboard led on to indicate read-only error
LED13.value = True
def __str__(self):
"""Serialize data for visualization on serial console"""
output = self.name + ":\n"
for item in self.data.items():
output += "\t{0}: {1}\n".format(*item)
return output
def __repr__(self):
return self.name
def update(self, current, verbose=True):
"""Read the data from sensors and update the data dict variable"""
if self.debug:
print("Update {} : Current : {} | Last : {} | Interval : {}".format(
self.name,
current,
self._last_update,
self.update_interval))
if current - self._last_update >= self.update_interval:
self._last_update = current
self._update()
if verbose:
print(self)
def _update(self):
"""Here comes specific update code"""
pass
@property
def json(self):
"""Serialized data to compact json-formatted string"""
output = '{"' + self.name + '":{'
first_data = True
for name, value in self.data.items():
if first_data:
comma = ""
first_data = False
else:
comma = ","
output = '{}{}"{}":"{}"'.format(output, comma, name, value)
output = output + '}}'
return output
def csv(self, header=False):
"""Serialized data values (or keys if header=True) as CSV line"""
if header:
return ";".join(self.data.keys())
return ";".join(self.data.values())
def write_on_flash(self, current, verbose=True):
"""Save the current data as csv file on SPI flash"""
if self.write_interval >= 0:
if self.debug:
print("Write {} : Current : {} | Last : {} | Interval : {}".format(
self.name,
current,
self._last_backup,
self.write_interval))
if current - self._last_backup >= self.write_interval:
self._last_backup = current
file_name = "{}.csv".format(self.name)
file_path = "data/"
try:
#Check if the file exists. If not, creates it with CSV header
if file_name not in os.listdir(file_path):
with open("/".join((file_path, file_name)), "w") as csv_file:
csv_file.write(self.csv(header=True))
csv_file.write("\n")
if verbose:
print("File created : {}".format(file_name))
with open("/".join((file_path, file_name)), "a") as csv_file:
csv_file.write(self.csv())
csv_file.write("\n")
if verbose:
print("Written data : \n{}".format(self.csv()))
except OSError as err:
print("Err {}: readonly".format(err))
# to avoid trying again till next reset
self.write_interval = -1
# Turn onboard led on to indicate read-only filesystem
LED13.value = True
def send_json(self, current, uart, verbose=True):
"""Send JSON string over UART"""
if self.debug:
print("Send {} : Current : {} | Last : {} | Interval : {}".format(
self.name,
current,
self._last_send,
self.send_interval))
if current - self._last_send >= self.send_interval:
self._last_send = current
uart.write(self.json)
uart.write("\n")
if verbose:
print(self.json)
#print("\n")
class SysData(Data):
"""Subclass for Feather board data"""
def __init__(self, name="SYS", update_interval=10, write_interval=300,
send_interval=60, debug=False):
Data.__init__(self, name=name,
update_interval=update_interval,
write_interval=write_interval,
send_interval=send_interval,
debug=debug)
self.data = {'time': "2000/01/01_00:00:00",
'vbat': int(),
'cput': float()}
# Battery voltage
self.vbat = AnalogIn(board.VOLTAGE_MONITOR)
self._rtc = rtc.RTC()
def _update(self):
""" Update data from Feather board"""
self.data['time'] = TIME_FORMAT.format(*self._rtc.datetime[0:6])
self.data['cput'] = round(microcontroller.cpu.temperature, 2)
# Note about v_bat calculation :
# 0.000100708 = 2*3.3/65536 with
# 2 : voltage is divided by 2
# 3.3 : Vref = 3.3V
# 65536 : 16bit ADC
val = 0
samples = 10
for sample in range(samples):
val += self.vbat.value
time.sleep(0.01)
self.data['vbat'] = round(val/samples*0.000100708, 3)
class BME280Data(Data):
"""Subclass for BME280 sensor"""
def __init__(self, name="BME", update_interval=10, write_interval=300,
send_interval=60, debug=False):
Data.__init__(self, name=name,
update_interval=update_interval,
write_interval=write_interval,
send_interval=send_interval,
debug=debug)
self.data = {'time' : "2000/01/01_00:00:00",
'temp': float(),
'hum': int(),
'press': float()}
# BME280 sensors (I2C)
# i2c addresses for BME280 breakout :
# 0x77 = adafruit breakout board
# 0x76 = tiny cheap chinese board
self.bme280 = Adafruit_BME280_I2C(I2C(board.SCL, board.SDA),
address=0x76)
def _update(self):
"""Update data from BME280"""
self.data['time'] = TIME_FORMAT.format(*time.localtime()[0:6])
self.data['temp'] = round(self.bme280.temperature, 1)
self.data['hum'] = int(self.bme280.humidity)
self.data['press'] = round(self.bme280.pressure, 2)
def rgb(self, neopixel_max=70):
"""Convert atmospheric data from BME280 sensor into NeoPixel color as
a tuple (RED, BLUE, GREEN):
* RED => temperature : max = 35degC, min =10degC (range 25°C)
* BLUE => humidity : max= 100%, mini=0%
* GREEN => pression : mini=960hPa, maxi = 1030hPa (range 70hPa)
"""
# RED componant calculation from temperature data
# 10 is the min temperature, 25 is the range
# (10+25=35°C = max temperature)
red = int((self.data['temp']-10)*neopixel_max/25)
if red > neopixel_max:
red = neopixel_max
if red < 0:
red = 0
# BLUE componant calculation: very simple! By definition relative
# humidity cannot be more than 100 or less than 0, physically
blue = int(self.data['hum']*neopixel_max/100)
# GREEN component calculation : 960 is the minimum pressure and 70 is
# the range (960+70 = 1030hPa = max pressure)
green = int((self.data['press']-960)*neopixel_max/70)
if green > neopixel_max:
green = neopixel_max
if green < 0:
green = 0
if self.debug:
print("Col:{}".format((red, green, blue)))
return (red, green, blue)
class GPSData(Data):
"""Sub class for GPS"""
def __init__(self, name="GPS",
update_interval=0, write_interval=60, send_interval=60,
enable=True, enable_pin=DigitalInOut(board.A5),
rtc=None, debug=False):
Data.__init__(self, name=name,
update_interval=update_interval,
write_interval=write_interval,
send_interval=send_interval,
debug=debug)
self.data = {'time': "2000/01/01_00:00:00",
'lat': float(),
'lon': float(),
'alt': float(),
'qual': int(),
'age': int()}
self._rtc = rtc
self._enable = enable
self._gps_last_fix = int()
self._gps_current_fix = int()
# Set the pin to control the power to GPS module
self._gps_en_pin = enable_pin
self._gps_en_pin.direction = Direction.OUTPUT
self._gps = GPS(UART(board.TX, board.RX, baudrate=9600, timeout=3000)) #, debug=self.debug)
if self._enable:
self.enable()
else:
self.disable()
def _update(self):
self._gps.update()
if self._enable:
self._gps_current_fix = int(time.monotonic())
self.data['time'] = TIME_FORMAT.format(
self._gps.timestamp_utc.tm_year,
self._gps.timestamp_utc.tm_mon,
self._gps.timestamp_utc.tm_mday,
self._gps.timestamp_utc.tm_hour,
self._gps.timestamp_utc.tm_min,
self._gps.timestamp_utc.tm_sec)
if self._rtc is not None:
set_clock_from_localtime(self._rtc)
if self._gps.has_fix:
self._gps_last_fix = self._gps_current_fix
self.data['lat'] = self._gps.latitude
self.data['lon'] = self._gps.longitude
self.data['alt'] = self._gps.altitude_m
self.data['qual'] = self._gps.fix_quality
self.data['age'] = 0
else:
self.data['age'] = int(self._gps_current_fix - self._gps_last_fix)
else:
self.data = {}
def enable(self):
"""Enable GPS module"""
# Set GPS module on FeatherWing board
if not self._enable:
self._enable = True
self._gps_en_pin.value = not self._enable # Set enable pin high to disable GPS module
time.sleep(0.1)
# Turn on the basic GGA and RMC info
self._gps.send_command('PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
self._gps.send_command('PMTK220,1000') # 1000 ms refresh rate
if self._rtc is not None:
rtc.set_time_source(self._rtc)
if self.debug:
print("GPS is the time source!")
def disable(self):
"""Disable GPS module"""
if self._enable:
self._enable = False
self._gps_en_pin.value = not self._enable # Set enable pin high to disable GPS module
# Switch backto internal RTC
rtc.set_time_source(rtc.RTC())
#############
# Functions #
#############
def rotate_files(last_time, debug=False):
"""Check if files need to rotate
=> every new hour
=> every new day
"""
current_time = time.localtime()
if debug:
print(last_time[3])
print(current_time[3])
# If the hour changed : copy current data.csv to hourly directory
if current_time[3] != last_time[3]:
print("Time to move hourly data !")
os.rename("data/data.csv", "data/hourly/{:02}.csv".format(last_time[3]))
# If the day changed : copy content of hourly to daily directories
if current_time[2] != last_time[2]:
print("Time to move daily data !")
# Create new dir for the date of yesterday
newdir = "data/daily/{}{:02}{:02}".format(*last_time[0:3])
os.mkdir(newdir)
# Move each "hourly file" to the new directory
for file in os.listdir('data/hourly'):
print("Move {} to {}".format(file, newdir))
os.rename("data/hourly/{}".format(file), "{}/{}".format(newdir, file))
# Finally update last_time, each time
return current_time
def set_clock_from_localtime(clock, treshold=2.0):
"""
Compare internal RTC and date-time from time.localtime() and set
the RTC date/time to this value if the difference is more or equal to
threshold (in seconds).
The source of time.localtime() can be set with rtc.set_time_source()
"""
# Max difference between GPS and internal RTC (in seconds):
if abs(time.mktime(time.localtime()) - time.mktime(clock.datetime)) >= treshold:
# print("Clock difference with GPS!")
# print("Previous date/time : " + TIME_FORMAT.format(*clock.datetime[0:6]))
clock.datetime = time.localtime() # Trust localtime if there is a bias
print("Clocks synced !")

View File

@ -45,329 +45,43 @@ __version__ = 0.2
####################### #######################
import time import time
import gc
import os
import rtc import rtc
import microcontroller import gc
import board import board
from busio import UART
# import micropython # import micropython
from busio import I2C, UART
from analogio import AnalogIn
from digitalio import DigitalInOut, Direction
from adafruit_bme280 import Adafruit_BME280_I2C
from adafruit_gps import GPS
import neopixel import neopixel
import cameteo
########## ##########
# config # # config #
########## ##########
print_data_flag = True # Print data on USB UART / REPL output ? PRINT_DATA = True # Print data on USB UART / REPL output ?
send_json_flag = True # Send data as JSON on second UART ? DATA_TO_NEOPIXEL = True # Display atmospheric data as color on onboard neopixel ?
backup_data_flag = True # Write data as CSV files on onboard SPI Flash ? GPS_ENABLE = True # Use GPS module ?
neopixel_flag = True # Display atmospheric data as color on onboard neopixel ?
gps_enable_flag = True # Use GPS module ?
UPDATE_INTERVAL = 10 # Interval between data acquisition (in seconds) UPDATE_INTERVAL = 10 # Interval between data acquisition (in seconds)
WRITE_INTERVAL = 60 # Interval between data written on flash Memory WRITE_INTERVAL = 60 # Interval between data written on flash Memory (-1 to disable)
SEND_INTERVAL = 60 # Interval between packet of data sent to Rpi SEND_INTERVAL = 60 # Interval between packet of data sent to Rpi (-1 to disable)
TIME_FORMAT = "{:04}/{:02}/{:02}_{:02}:{:02}:{:02}" # Date/time format
NEOPIXEL_MAX_VALUE = 70 # max value instead of brightness to spare some mem NEOPIXEL_MAX_VALUE = 70 # max value instead of brightness to spare some mem
###########
# Classes #
###########
class Data:
"""Class for handling data"""
def __init__(self):
self.data = {'SYS': {'time': "2000/01/01_00:00:00",
'vbat': int(),
'cput': float()},
'BME': {'temp': float(),
'hum': int(),
'press': float()},
'GPS': {'time': "2000/01/01_00:00:00",
'lat': float(),
'lon': float(),
'alt': float(),
'qual': int(),
'age': int()}}
self._gps_last_fix = int()
self._gps_current_fix = int()
def update(self):
"""Read the data from various sensors and update the data dict variable"""
# Data from Feather board
self.data['SYS']['time'] = TIME_FORMAT.format(*clock.datetime[0:6])
self.data['SYS']['vbat'] = round(measure_vbat(), 3)
self.data['SYS']['cput'] = round(microcontroller.cpu.temperature, 2)
# Data from BME280
self.data['BME']['temp'] = round(bme280.temperature, 1)
self.data['BME']['hum'] = int(bme280.humidity)
self.data['BME']['press'] = round(bme280.pressure, 2)
if gps_enable_flag:
self._gps_current_fix = int(time.monotonic())
self.data['GPS']['time'] = TIME_FORMAT.format(
gps.timestamp_utc.tm_year,
gps.timestamp_utc.tm_mon,
gps.timestamp_utc.tm_mday,
gps.timestamp_utc.tm_hour,
gps.timestamp_utc.tm_min,
gps.timestamp_utc.tm_sec)
if gps.has_fix:
self._gps_last_fix = self._gps_current_fix
self.data['GPS']['lat'] = gps.latitude
self.data['GPS']['lon'] = gps.longitude
self.data['GPS']['alt'] = gps.altitude_m
self.data['GPS']['qual'] = gps.fix_quality
self.data['GPS']['age'] = 0
else:
self.data['GPS']['age'] = int(self._gps_current_fix
- self._gps_last_fix)
else:
self.data['GPS'] = None
def show(self):
"""Serialize data for visualization on serial console"""
for source, val in self.data.items():
print(source + ": ")
if val is not None:
for name, value in val.items():
print("\t{0}: {1}".format(name, value))
@property
def json(self):
"""Serialized data to compact json-formatted string"""
output = '{'
first_source = True
for source, val in self.data.items():
if first_source:
first_source = False
comma_src = ""
else:
comma_src = ","
output = '{}{}"{}":'.format(output, comma_src, source)
if val is not None:
output = output + '{'
first_data = True
for name, value in val.items():
if first_data:
comma = ""
first_data = False
else:
comma = ","
output = '{}{}"{}":"{}"'.format(output, comma, name, value)
output = output + '}'
output = output + '}'
return output
@property
def rgb(self):
"""Convert atmospheric data from BME280 sensor into NeoPixel color as
a tuple (RED, BLUE, GREEN):
* RED => temperature : max = 35degC, min =10degC (range 25°C)
* BLUE => humidity : max= 100%, mini=0%
* GREEN => pression : mini=960hPa, maxi = 1030hPa (range 70hPa)
"""
# RED componant calculation from temperature data
# 10 is the min temperature, 25 is the range
# (10+25=35°C = max temperature)
red = int((self.data['BME']['temp']-10)*NEOPIXEL_MAX_VALUE/25)
if red > NEOPIXEL_MAX_VALUE:
red = NEOPIXEL_MAX_VALUE
if red < 0:
red = 0
# BLUE componant calculation: very simple! By definition relative
# humidity cannot be more than 100 or less than 0, physically
blue = int(self.data['BME']['hum']*NEOPIXEL_MAX_VALUE/100)
# GREEN component calculation : 960 is the minimum pressure and 70 is
# the range (960+70 = 1030hPa = max pressure)
green = int((self.data['BME']['press']-960)*NEOPIXEL_MAX_VALUE/70)
if green > NEOPIXEL_MAX_VALUE:
green = NEOPIXEL_MAX_VALUE
if green < 0:
green = 0
if print_data_flag:
print("Col:{}".format((red, green, blue)))
return (red, green, blue)
def write_on_flash(self):
"""Save the current data as csv file on SPI flash"""
global backup_data_flag
try:
with open("data/data.csv", "a") as csv_file:
if gps_enable_flag:
csv_file.write("{};{};{};{};{};{};{};{};{};{}\n".format(
self.data['SYS']['time'],
self.data['BME']['temp'],
self.data['BME']['hum'],
self.data['BME']['press'],
self.data['SYS']['vbat'],
self.data['GPS']['time'],
self.data['GPS']['lon'],
self.data['GPS']['lat'],
self.data['GPS']['alt'],
self.data['GPS']['qual'],
))
else:
csv_file.write("{};{};{};{};{};;;;;\n".format(
self.data['SYS']['time'],
self.data['BME']['temp'],
self.data['BME']['hum'],
self.data['BME']['press'],
self.data['SYS']['vbat'],
))
except OSError as err:
print("Err {}: readonly".format(err))
# to avoid trying again till next reset
backup_data_flag = False
# Turn onboard led on to indicate read-only error
led13.value = True
#############
# Functions #
#############
def check_data_dir():
"""Check if data directories exists"""
global backup_data_flag
try:
if 'data' not in os.listdir():
os.mkdir('data')
os.mkdir('data/hourly')
os.mkdir('data/daily')
elif 'hourly' not in os.listdir('data'):
os.mkdir('data/hourly')
elif 'daily' not in os.listdir('data'):
os.mkdir('data/daily')
except OSError as err:
print("Err {}: readonly".format(err))
backup_data_flag = False # to avoid trying again till next reset
# Turn onboard led on to indicate read-only error
led13.value = True
def rotate_files(last_time):
"""Check if files need to rotate
=> every new hour
=> every new day
"""
current_time = clock.datetime
# If the hour changed : copy current data.csv to hourly directory
if current_time[3] != last_time[3] and backup_data_flag:
print("Time to move hourly data !")
os.rename("data/data.csv", "data/hourly/{:02}.csv".format(last_time[3]))
# If the day changed : copy content of hourly to daily directories
if current_time[2] != last_time[2] and backup_data_flag:
print("Time to move daily data !")
# Create new dir for the date of yesterday
newdir = "data/daily/{}{:02}{:02}".format(*last_time[0:3])
os.mkdir(newdir)
# Move each "hourly file" to the new directory
for file in os.listdir('data/hourly'):
print("Move {} to {}".format(file, newdir))
os.rename("data/hourly/{}".format(file), "{}/{}".format(newdir, file))
# Finally update last_time, each time
return current_time
def set_clock_from_gps(treshold=5.0):
"""Compare internal RTC and date-time from GPS (if enable and fixed) and set
the RTC date time to GPS date-time if the difference is more or equal to
threshold (in seconds)"""
if gps_enable_flag:
gps_datetime = time.struct_time((gps.timestamp_utc.tm_year,
gps.timestamp_utc.tm_mon,
gps.timestamp_utc.tm_mday,
gps.timestamp_utc.tm_hour,
gps.timestamp_utc.tm_min,
gps.timestamp_utc.tm_sec, 0, 0, 0))
# Max difference between GPS and internal RTC (in seconds):
if abs(time.mktime(gps_datetime) - time.mktime(clock.datetime)) >= treshold:
# print("Clock difference with GPS!")
# print("Previous date/time : " + TIME_FORMAT.format(*clock.datetime[0:6]))
clock.datetime = gps_datetime # Trust GPS if there is a bias
print("Clocks synced !")
def measure_vbat(samples=10, timestep=0.01):
"""Measure Vbattery as the mean of n samples with timestep second between
each measurement"""
# Note about v_bat calculation :
# 0.000100708 = 2*3.3/65536 with
# 2 : voltage is divided by 2
# 3.3 : Vref = 3.3V
# 65536 : 16bit ADC
val = 0
for sample in range(samples):
val = val + vbat.value
time.sleep(timestep)
return val/samples*0.000100708
######### #########
# Setup # # Setup #
######### #########
gc.collect() gc.collect()
# micropython.mem_info() # micropython.mem_info()
# Enable RTC of the feather M0 board # Enable RTC of the feather M0 board
clock = rtc.RTC() CLOCK = rtc.RTC()
# clock.datetime = time.struct_time((2018, 7, 29, 15, 31, 30, 0, 0, 0))
# BME280 sensors (I2C)
i2c = I2C(board.SCL, board.SDA)
# i2c addresses for BME280 breakout :
# 0x77 = adafruit breakout board
# 0x76 = tiny chinese board
bme280 = Adafruit_BME280_I2C(i2c, address=0x76)
# Battery voltage
vbat = AnalogIn(board.VOLTAGE_MONITOR)
# Set the pin to control the power to GPS module
gps_en_pin = DigitalInOut(board.A5)
gps_en_pin.direction = Direction.OUTPUT
# Set the pin N°13 to use the onboard LED as read-only/read-write indicator
led13 = DigitalInOut(board.D13)
led13.direction = Direction.OUTPUT
led13.value = False
# Set GPS module on FeatherWing board
gps_en_pin.value = not gps_enable_flag # Set enable pin high to disable GPS module
if gps_enable_flag:
gps_uart = UART(board.TX, board.RX,
baudrate=9600, timeout=3000)
gps = GPS(gps_uart)
# Turn on the basic GGA and RMC info
gps.send_command('PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
gps.send_command('PMTK220,1000') # 1000 ms refresh rate
# Second UART to communicate with raspberry pi (throught GPIO) # Second UART to communicate with raspberry pi (throught GPIO)
if send_json_flag: if SEND_INTERVAL >= 0:
rpi_uart = UART(board.A2, board.A3, rpi_uart = UART(board.A2, board.A3, baudrate=115200, timeout=2000)
baudrate=115200, timeout=2000)
# Set onboard Neopixel : used as atmo data output # Set onboard Neopixel : used as atmo data output
# brightness is fixed to 1 to spare some memory. Use NEOPIXEL_MAX_VALUE instead # brightness is fixed to 1 to spare some memory. Use NEOPIXEL_MAX_VALUE instead
if neopixel_flag: if DATA_TO_NEOPIXEL:
pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=1) pixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=1)
else: else:
# if neopixel is disable : turn off the LED # if neopixel is disable : turn off the LED
@ -375,45 +89,37 @@ else:
pixel[0] = (0, 0, 0) pixel[0] = (0, 0, 0)
pixel = None pixel = None
# Finally check if data directories exist sys_data = cameteo.SysData(debug=False)
check_data_dir() bme_data = cameteo.BME280Data()
gps_data = cameteo.GPSData(rtc=CLOCK)
data = [gps_data, sys_data, bme_data]
# Init timers
last_update = last_sent_packet = last_written_data = time.monotonic()
last_rotation = time.localtime()
############# #############
# Main loop # # Main loop #
############# #############
data = Data()
# Init timers
last_update = last_sent_packet = last_written_data = time.monotonic()
last_rotation = clock.datetime
while True: while True:
if gps_enable_flag: current_time = time.monotonic()
gps.update()
set_clock_from_gps()
current = time.monotonic() for src in data:
if current - last_update >= UPDATE_INTERVAL: src.update(current_time, verbose=PRINT_DATA)
last_update = current
data.update()
if print_data_flag:
data.show()
if neopixel_flag:
pixel[0] = data.rgb
if send_json_flag and current - last_sent_packet >= SEND_INTERVAL:
last_sent_packet = current
rpi_uart.write(data.json + '\n')
print(data.json + '\n')
if backup_data_flag and current - last_written_data >= WRITE_INTERVAL:
last_written_data = current
# First check if files need to rotate # First check if files need to rotate
last_rotation = rotate_files(last_rotation) last_rotation = cameteo.rotate_files(last_rotation, debug=True)
print("Backup data...")
data.write_on_flash() for src in data:
src.write_on_flash(current_time)
src.send_json(current_time, uart=rpi_uart, verbose=PRINT_DATA)
# # First check if files need to rotate
# last_rotation = rotate_files(last_rotation)
gc.collect() gc.collect()
# micropython.mem_info(1) # micropython.mem_info(1)