Wiznet makers

mark

Published October 13, 2023 © Apache License 2.0 (Apache-2.0)

62 UCC

8 WCC

40 VAR

0 Contests

0 Followers

0 Following

Screen Time monitoring

Screen Time monitoring

COMPONENTS Hardware components

WIZnet - W5100S-EVB-Pico

x 1

Software Apps and online services

x 1


Adafruit - Circuitpython

x 1


PROJECT DESCRIPTION
As a Enginneer, I always work in the office to handle a lot of documentation and programming in front of the computer or monitor. There always have a time that I have lost track on my work that causes putting too much of time on job and forget to take some rest.
Thus, I had tried to make an application to monitor my screen time with my PC to check my screening time.
This application should helped me a lot of manage my time to allow me to take care with my eye.
This is just a simple application to work with my application based on W5100S-EVB-PICO and Adafruit IO in circuitpython.
The HLK-LD2410B human detection sensor module that I'm using has a lot of features to allow you to develop different kinds of application. For this applicaiton, I could upgrade it to have a security feature to prevent someone else is using my PC.
For details, please refer to the information below.
Structure:

As you see from the image above, this application is a simple application that only required two modules with a W5100S-EVB-PICO to mange the data for delivering important information to the Adafruit IO.
The first module is the most important module for this application. The Human detection sensor module (HLK-LD2410B) has the ability to determine and differentiate a moving object and a stable object distance with the sensor. This information will send in a standard formatted byte string through UART.
The next module is a WS2812 LED lights (Neopixel library compatiable) that I found from the internet. It will display in different kind of colors to alert me to leave the PC and take some rest.
Programming - Human Detection sensor
For communicating with this module using UART, it is a complicated job for me. However, the user manaul and the help of ChatGPT had helped a lot on this issue.
They provide good guidelines and description for me to have a better understanding of this module and making codes in different kind way.
The followings are the library's functions that I had made for this module to fulfill the usage for this application.
1. Collect Data - Change those Byte information into meaningful information to Adafruit IO and used for displaying the LED status.
2. Send commands - Sending commands to the module and make some modification to the module.
3. Search and identfy data - Find those data using head and tail of the data set and send it to the above application for analysis
Progamming - Collect data
Collecting data is simple to handle. I just need to found the data from my module and collect the correct part of information from those data.
All those data will be converted back into integers and saved in each variables to allow my W5100S-EVB-PICO could be used for further operation or devliery it to Adafruit IO.
Codes
def collect_data (self, timeout: int = 5) -> None:
        self.uart.reset_input_buffer()
        gc.collect()
        stamp = time.monotonic()
        response = b""
        while (time.monotonic() - stamp) < timeout:
            if self.uart.in_waiting:
                response += self.uart.read(1)
                if Out_head in response and response[-4:] == Out_end:
                    break
        #print(response)
        
        #check data mode headers
        self._check_head_tail("data",response)
        if response[7] != int.from_bytes(Out_data_head,'big'):
            raise ValueError ("Wrong Output data Header")
        elif response[-6] != int.from_bytes(Out_data_end,'big'):
            raise ValueError ("Wrong Output data End")
        elif response[-5] != 0:
            raise ValueError ("Wrong checking point")
        
        #Checking operation Mode
        if response[6] == 2:
            self.W_type = "Basic Mode"
        elif response[6] == 1:
            self.W_type = "Engineering Mode"
        else:
            raise ValueError ("Wrong Working mode Data")
        
        #Checking target:
        if response[8] == 0:
            self.target = "No target"
        elif response[8] == 1:
            self.target = "Moving target"
        elif response[8] == 2:
            self.target = "Stable target"        
        elif response[8] == 3:
            self.target = "Both target"            
        else:
            raise ValueError ("Wrong Rarget Value")
        
        #Convert all the data
        self.move_dist = response[10]*256 + response[9]
        self.move_sen = response[11]
        self.stable_dist = response[13]*256 + response[12]
        self.stable_sen = response[14]
        self.M_dist = response[16]*256 + response[15]
Programming - Send commands
This modules has a lot of commands to be set. Like controlling the bluetooth of this module, setting the sensitivity of the module or operting in engineer mode for this application.
Since my application is a simple application, it does not required those specific settings to provide a very accurate information.
Thus, I just select some of the commands that is required for my application.
1. On/ Off command mode - This module required the user to send out a set of command to alert the module to change into command mode to handle different kinds of command.
Codes
@property
    def cmd_mode(self): #check the command mode is it on / off 
        return self.cmd_check # 0 = OFF , 1 = ON
    
    @cmd_mode.setter #set the command mode
    def cmd_mode(self, value):
        #Set the codes into correct format before send 
        if value == 1: 
            data = Command_head + self._shifting(4) +Command_mode_on + Command_end
        elif value == 0:
            data = Command_head + self._shifting(2) +Command_mode_off + Command_end
        
        result =self._send_command(data) #send the data to the module
        
        #Received feedback - Print out the result and change the command mode staus
        if result[0] == Command_mode_on[0]: 
            print("Command Mode ON")
            self.cmd_check = 1
        elif result[0] == Command_mode_off[0]:
            print("Command Mode OFF")
            self.cmd_check = 0
2. Set Sensitivty - For my applicaton, it is required me to set the sensitivity for each distance units. By setting these information, I could set the sensor to focus on specific distance.
Codes
def set_sensitivity(self,unit_sen: str, move_sen: int, stable_sen: int):
        padding = b"\x00\x00"
        move = b"\x01\x00"
        stable = b"\x02\x00"
        try:
            unit = self._shifting(int(unit_sen))
            
        except ValueError as error:
            if unit_sen is "all":
                unit = b"\xFF\xFF"
            else:
                raise ValueError ("Wrong sensitivity Input")
            
        except Exception as error:
            raise error
        
        data = Command_head + self._shifting(20) + sensitivity + padding + unit + padding
        data = data + move + self._shifting(move_sen) + padding
        data = data + stable + self._shifting(stable_sen) + padding + Command_end
        
        result =self._send_command(data)
        
        print ("Sensitivity has been Set!")
3. Send command and collect responses - This section is used to organize all commands into one standard function. It will help me send those commands and collects the response message and return it back to each commands for further analysis.
Codes
def _send_command (self,cmd: str, timeout: int = 5) -> None:
        #print(cmd)
        self.uart.write(cmd)
        
        self.uart.reset_input_buffer()
        gc.collect()
        stamp = time.monotonic()
        response = b""
        while (time.monotonic() - stamp) < timeout:
            if self.uart.in_waiting:
                response += self.uart.read(1)
                if Command_head in response and response[-4:] == Command_end:
                    break
        #print(response)
        #print(type(response))
        
        self._check_head_tail("command",response)
        if response[6] + response[7] != cmd[6] + cmd[7] + 1:
            raise ValueError("Command Word Response Error")
        elif response[8] + response[9] != 0:
            raise ValueError("Command Fail")
        
        return response[6:-4]
Programming - Search and identfy data
The most complication data handling is finding the those data from the load of information provided by the module.
As I mentioned previously, this module will automatically send out the data to W5100S-EVB-PICO. I need to collect the correct data from the UART buffer and converted into correct information.
Thus, checking those data based on header and tails are the best way to easily find those information. I had even added check points to ensure those information are correct and return the core information for further progress.
Codes
def _check_head_tail(self,mode, response) -> None:
        if mode is "data":
            head = Out_head
            tail = Out_end
            output = "Output"
        elif mode is "command":
            head = Command_head
            tail = Command_end
            output = "Command"
        else:
            raise ValueError ("Wrong head tail mode Input")
        
        if response[0:4] != head:
            raise ValueError ("Wrong {} Header".format(output))
        elif response[-4:] != tail:
            raise ValueError ("Wrong {} End".format(output))
        elif response[5]*256 + response[4] != len(response) - len(Out_head) - len(Out_end) - 2:
            raise ValueError ("Missing {} data".format(output))
Programming - Main
The main code is simple. I had used WIZnet's Adafruit IO reference code to alllow me easily to communicate with Adafruit IO using W5100S-EVB-PICO.
For organize the between the module and what kind of information will be uploaded to Adafruit IO will shows as follow.
1. Detect are there any person in front of the sensor
2. Repeating Number 1 until it has any condition change
a) Cannot detect anyone in front of the screen
b) Waited for a long time
3. If any above condition has activated, it will handle in
a) Get the leaving time and compare the waited time and reset all variables
b) Showed in Red light and upload alert to Adafruit
3. The waiting time will be handled and set by Adafruit IO.
- It includes a reset button to reset all the variables
Codes:
#function to collect the screen time from adafruit IO
def waiting_time(client, topic, message):
    global time_recorder
    # Method callled when a client's subscribed feed has a new value.
    print("New message 2 on topic {0}: {1}".format(topic, message))
    time_recorder = int(message)

#function to collect reset response from adafruit IO
def reset(client, topic, message):
    # Method callled when a client's subscribed feed has a new value.
    print("New message on topic {0}: {1}".format(topic, message))
    if message == "0":
        flag.value = 1

#Main function to manage the screen time operation   
def scan(time_value,temp,counter,alert_counter,leave, led):
    global time_recorder
    
    #collect data function from LD2410B module library
    dist_sen.collect_data()
    #check the sensor's targetting object
    if dist_sen.target == "Both target":
        if dist_sen.move_dist >= dist_sen.stable_dist: #compare the distance - distance value is more accurate
            data = dist_sen.move_dist
        else:
            data = dist_sen.stable_dist 
    elif dist_sen.target == "Moving target": #If it detects the moving object, choose moving target
        data = dist_sen.move_dist
    elif dist_sen.target == "Stable target": #Since the distance between the module is short, stable target could be consider to use.
        data = dist_sen.stable_dist
        if data == 8: #if it shows 8 value, it means error.
            data = dist_sen.move_dist
    else:
        data = None # No target, just ignore
    
    if data != None
        # the range that I will be seated - If detected
        if data > 65 and data < 100: 
            leave = False # Turn off leave flag
            temp = time.time() #collect current time
            if time_value is None:
                time_value = temp # Starting Point 
            else: #check current status for LED display
                difference = temp - time_value
                if difference <= (time_recorder /3): #Beginning period with the screen
                    print ("Green Light")
                    pixels.fill((38,252,5)) # Display green light
                    pixels.show() 
                    led += 1
                    if led == 1: #Upload once to adafruit IO
                        io.publish("light", "#26fc05") #Upload green light status
                elif difference > (time_recorder /3) and difference < time_recorder: #it has been for a while with the screen
                    print ("Yellow Light")
                    if led >= 1:
                        led = 0
                    pixels.fill((250,242,7)) # Display Yellow Light
                    pixels.show() 
                    led -= 1
                    if led == -1: #Upload once to adafruit IO
                        io.publish("light", "#faf207")
                elif difference >= time_recorder: #If it has passed or equal to the waiting time
                    print("Over time! - Red Light") 
                    alert_counter += 1
                    if alert_counter == 1: #Activate the alert section
                        io.publish("alert", "OverTime") #posted to adafruit IO to show it has passed the screening time
                        io.publish("light", "#fc0905") #showed red on adafruit IO
                    pixels.fill((252,9,5)) # sDisplay Red Light
                    pixels.show() 
        else: #detected No one is in front of the screen
            if leave is False: #confirmed the previous moments are present in front of the screen 
                counter += 1 
                if temp is not None: #if the previous moment is present in front of the screen
                    counter  = 0 #reset counter
                    temp = None
                print (counter)
                if counter > 3: #if accumulated for 3 times, confirmed no one is in front of the screen.
                    #reset everything
                    temp = time.time()
                    difference = temp - time_value
                    print ("You have looked on the screen for {} seconds".format(difference))
                    counter = 0
                    time_value = None
                    temp = None
                    alert_counter = 0
                    leave = True
                    led = 0
                    pixels.fill((0,0,0)) # set to turn off the pixel
                    pixels.show() #present to off
                    io.publish("light", "#000000") 
                
    return time_value, temp, counter, alert_counter,leave,led
Results
The followings are the result from Adafruit IO and the display results on LED.

From the image above, it shows I had set a reset button to reset all the variables inside W5100S-EVB-PICO. It also includes Screen Time setup that is in seconds.
If you wanted to make it in a longer period, you could based on my codes to make your own changes. Please know that my codes is based on "Seconds" as the based unit for this code.
LED display in different condition:
LED off - The device has been reset or there is no one in front of the sensor

Green - The beginning stage of screen time.

Yellow - Screen time has been passed for a long period of time:

Red - It has passed the screen time and it sent alert to Adafruit IO

As you could see from the above images, it shows the application will provide different kind of LED to indicate me the screen time that I had been in front my computer.
This information will be uploaded to the color block that showed on Adafruit IO.
The alert message will also shows a "Overtime" message on alert message block to notify me.
YouTube:
Finally, I had made a simple YouTube demo based on this applcation.

 
Documents
  • codes

Comments Write