MicroPython, multiplexing a 7 segment display, requests, loop issue - while-loop

I've tried many variations of this project. The end goal being, a Raspberry Pi Pico W running Micropython, and a common cathode 4 digit, 7 segment display (NOT a TM1637 type, that will be the last resort, but there has to be a way to do it without, and I'm trying to figure it out), which will gather temperature data from an API, and then multiplex that data to display properly, while continuing to monitor the temp value.
The issue comes in that when I call back to urequests.get(temp_url).ujson() to gather the data again, that command causes the display temporarily go black until that block of code passes, but there's no other way I can imagine to keep getting the temp values repeatedly that either doesn't also cause a halt in the multiplexing loop, or doesn't update past the first get of the API data.
So here's the section of code that I'm struggling with. The main function just calls to connect and has some exception code, and the other exceptions have been removed for the purpose of diagnosing for now. This is definitely still a work in progress, ignore any indenting issues that were caused by copying and pasting, and remember this is file that has been changed and altered and tweaked to try to get results different ways, but all of my attempts seem to result in the same issue.
refresh = 0.0025
and dig1-4, SegDisplay, are all referencing part of the code for setting the digits and the outputting to the acceptable pins to produce the number.
PL1-4 are just being used to split up the temp value into it's up to 4 individual numbers, to then be used to loop through at the refresh speed. This part of the code works fine, if I set a static number, it works well, but once the number needs to be checked at requests (urequests and ujson as requests and json), the display goes black for half a second or so and then comes back on and loops everytime it checks back to r=requests.get.....
def display():
dig1.value(1)
dig2.value(1)
dig3.value(1)
dig4.value(1)
while True:
value = get_temp()
value = round(value)
value = str(value)
if value == None:
clear()
break
elif len(value)==1:
pl1 = "0"
pl2 = "0"
pl3 = "0"
pl4 = value[0]
dig1.value(1)
dig2.value(1)
dig3.value(1)
dig4.value(1)
SegDisplay(pl4)
dig4.value(0)
sleep(refresh)
dig4.value(1)
elif len(value)==2:
value = get_temp()
print(value)
value = round(value)
value = str(value)
pl1 = "0"
pl2 = "0"
pl3 = value[0]
pl4 = value[1]
dig1.value(1)
dig2.value(1)
dig3.value(1)
dig4.value(1)
SegDisplay(pl3)
dig3.value(0)
sleep(refresh)
dig3.value(1)
SegDisplay(pl4)
dig4.value(0)
sleep(refresh)
dig4.value(1)
elif len(value)==3:
pl1 = "0"
pl2 = value[0]
pl3 = value[1]
pl4 = value[2]
dig1.value(1)
dig2.value(1)
dig3.value(1)
dig4.value(1)
SegDisplay(pl2)
dig2.value(0)
sleep(refresh)
dig2.value(1)
SegDisplay(pl3)
dig3.value(0)
sleep(refresh)
dig3.value(1)
SegDisplay(pl4)
dig4.value(0)
sleep(refresh)
dig4.value(1)
elif len(value)==4:
pl1 = value[0]
pl2 = value[1]
pl3 = value[2]
pl4 = value[3]
SegDisplay(pl1)
dig1.value(0)
sleep(refresh)
dig1.value(1)
SegDisplay(pl2)
dig2.value(0)
sleep(refresh)
dig2.value(1)
SegDisplay(pl3)
dig3.value(0)
sleep(refresh)
dig3.value(1)
SegDisplay(pl4)
dig4.value(0)
sleep(refresh)
dig4.value(1)
def get_temp():
r = requests.get(temp_url).json
temp = r.get('ispoint')
return temp
def connect():
#Connect to WLAN
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(ssid, password)
while wlan.isconnected() == False:
print('Waiting to connect...')
sleep(1)
ip = wlan.ifconfig()[0]
print(f'Connected on {ip}')
led.value(1)
display() #CALL DISPLAY
return ip
Things to remember: This all is done on Micropython. I couldn't find any prebuilt libraries for 4 digit 7 segment displays that aren't the TM1637 types. And again, that part seems to work, it's just when I try to call to r=requests.get(temp_url).json() that it stops the multiplexing loops for a split second which obviously makes the display visibly flash dark.
Sorry this is a bit confusing. Just looking to see if anyone has any ideas that I'm missing to try to get a smooth display multiplexing loop while also checking the temp from the API in the same loop.

You could try using uasyncio (https://github.com/peterhinch/micropython-async). This module is standard installed in micropython and made to handle such issues.

Related

User Input only at the end of while loop

I want to make the "continue playing" user input to only appear when all the tries are exhausted in the guessing game.
from random import random, randint
import random
#Guessing Game
attempts = 5
print("===GUESS THE NUMBER===")
print(f"GAME OBJECTIVE: Guess the computer's number in {attempts} tries")
running = True
computerNum = random.randint(1,15)
while running == True:
guess = int(input("Guess the number:\n>"))
print("\n")
if guess > computerNum:
print("Lower...")
attempts -= 1
print(f"[You have {attempts} tries left]")
if guess < computerNum:
print("Higher...")
attempts -= 1
print(f"[You have {attempts} tries left]")
if attempts == 0:
print("GAME OVER!!!")
print(f"The computer's number is {computerNum}")
break
if guess == computerNum:
print("You have guessed the number!!!")
print("YOU WIN!!!")
break
print("\n")
playAgain = input("Do you want to continue playing(Y/N):\n")
if playAgain in ("N","n"):
break
The problem with this code now is just that each time you guess the number, it keeps asking if you want to continue playing.
Also, I want the console to print "Not a number" each time anything apart from a number is input.
if guess != int():
print("Enter a number")
This is my rough idea so far but I keep getting error messages
I am a beginner by the way
You can handle user input that is not a number as shown in https://www.geeksforgeeks.org/python-int-function/.
You should not use break when the user wins or loses.
If you use it, the user is not able to play the game again.
To prevent the program from asking every time whether the user wants to play again, you have to check again for remaining tries if attempts == 0 or guess == computerNum.
from random import random
import random
# Guessing Game
attempts = 5
print("===GUESS THE NUMBER===")
print(f"GAME OBJECTIVE: Guess the computer's number in {attempts} tries")
running = True
computerNum = random.randint(1, 15)
while running == True:
try:
guess = int(input("Guess the number:\n>"))
except ValueError as e:
print("Enter a number")
continue
print("\n")
if guess > computerNum:
print("Lower...")
attempts -= 1
print(f"[You have {attempts} tries left]")
if guess < computerNum:
print("Higher...")
attempts -= 1
print(f"[You have {attempts} tries left]")
if attempts == 0:
print("GAME OVER!!!")
print(f"The computer's number is {computerNum}")
if guess == computerNum:
print("You have guessed the number!!!")
print("YOU WIN!!!")
if attempts == 0 or guess == computerNum:
print("\n")
playAgain = input("Do you want to continue playing(Y/N):\n")
if playAgain in ("N", "n"):
break
else:
computerNum = random.randint(1, 15)
attempts = 5
Like Tacconinja007 said, you could use a try, except statement to make sure the guess is always a number. And if you want to check if the guess is an integer you could do:
if type(guess) == int:
And if you want to make it so the program asks the user if they want to play again once they run out of attempts you could try the following code:
from random import random, randint
import random
# Guessing Game
attempts = 5
print("===GUESS THE NUMBER===")
print(f"GAME OBJECTIVE: Guess the computer's number in {attempts} tries")
running = True
computerNum = random.randint(1, 15)
while running:
try:
guess = int(input("Guess the number:\n>"))
print("\n")
except ValueError:
print("Enter a number")
continue
if guess > computerNum:
print("Lower...")
attempts -= 1
print(f"[You have {attempts} tries left]")
elif guess < computerNum:
print("Higher...")
attempts -= 1
print(f"[You have {attempts} tries left]")
if attempts == 0:
print("GAME OVER!!!")
print(f"The computer's number is {computerNum}")
replay = input("Do you want to play again? (Y/N): ")
if replay in ("N", "n"):
break
else:
attempts = 5
computerNum = random.randint(1, 15)
if guess == computerNum:
print("You have guessed the number!!!")
print("YOU WIN!!!")
break
print("\n")

How to use hover events in mpl_connect in matplotlib

I'm working on line plotting a metric for a course module as well as each of its questions within a Jupyter Notebook using %matplotlib notebook. That part is no problem. A module has typically 20-35 questions, so it results in a lot of lines on a chart. Therefore, I am plotting the metric for each question in a low alpha and I want to change the alpha and display the question name when I hover over the line, then reverse those when no longer hovering over the line.
The thing is, I've tried every test version of interactivity from the matplotlib documentation on event handling, as well as those in this question. It seems like the mpl_connect event is never firing, whether I use click or hover.
Here's a test version with a reduced dataset using the solution to the question linked above. Am I missing something necessary to get events to fire?
def update_annot(ind):
x,y = line.get_data()
annot.xy = (x[ind["ind"][0]], y[ind["ind"][0]])
text = "{}, {}".format(" ".join(list(map(str,ind["ind"]))),
" ".join([names[n] for n in ind["ind"]]))
annot.set_text(text)
annot.get_bbox_patch().set_alpha(0.4)
def hover(event):
vis = annot.get_visible()
if event.inaxes == ax:
cont, ind = line.contains(event)
if cont:
update_annot(ind)
annot.set_visible(True)
fig.canvas.draw_idle()
else:
if vis:
annot.set_visible(False)
fig.canvas.draw_idle()
module = 'bd2bc472-ee0d-466f-8557-788cc6de3018'
module_metrics[module] = {
'q_count': 31,
'sequence_pks': [0.5274546300604932,0.5262044653349001,0.5360993905297703,0.5292329279700655,0.5268691588785047,0.5319099014547161,0.5305164319248826,0.5268235294117647,0.573648805381582,0.5647933116581514,0.5669839795681448,0.5646591970121382,0.5663157894736842,0.5646976090014064,0.5659005628517824,0.5693634879925391,0.5728268468888371,0.5668834184858337,0.5687237026647967,0.5795640965549567,0.5877684407096172,0.585690904839841,0.5766899766899767,0.5971341320178529,0.6059972105997211,0.6055516678329834,0.6209865053513262,0.6203121360354065,0.6153666510976179,0.6236909471724459,0.6387654898293196],
'q_pks': {
'0da04f02-4aad-4ac8-91a5-214862b5c0d0': [0.6686046511627907,0.6282051282051282,0.76,0.6746987951807228,0.7092198581560284,0.71875,0.6585365853658537,0.7070063694267515,0.7171052631578947,0.7346938775510204,0.7737226277372263,0.7380952380952381,0.6774193548387096,0.7142857142857143,0.7,0.6962962962962963,0.723404255319149,0.6737588652482269,0.7232704402515723,0.7142857142857143,0.7164179104477612,0.7317073170731707,0.6333333333333333,0.75,0.7217391304347827,0.7017543859649122,0.7333333333333333,0.7641509433962265,0.6869565217391305,0.75,0.794392523364486],
'10bd29aa-3a26-49e6-bc2c-50fd503d7ab5': [0.64375,0.6014492753623188,0.5968992248062015,0.5059523809523809,0.5637583892617449,0.5389221556886228,0.5576923076923077,0.51875,0.4931506849315068,0.5579710144927537,0.577922077922078,0.5467625899280576,0.5362318840579711,0.6095890410958904,0.5793103448275863,0.5159235668789809,0.6196319018404908,0.6143790849673203,0.5035971223021583,0.5897435897435898,0.5857142857142857,0.5851851851851851,0.6164383561643836,0.6054421768707483,0.5714285714285714,0.627906976744186,0.5826771653543307,0.6504065040650406,0.5864661654135338,0.6333333333333333,0.6851851851851852]
}}
suptitle_size = 24
title_size = 18
tick_size = 12
axis_label_size = 15
legend_size = 14
fig, ax = plt.subplots(figsize=(15,8))
fig.suptitle('PK by Sequence Order', fontsize=suptitle_size)
module_name = 'Test'
q_count = module_metrics[module]['q_count']
y_ticks = [0.0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]
x_ticks = np.array([x for x in range(0,q_count)])
x_labels = x_ticks + 1
# Plot it
ax.set_title(module_name, fontsize=title_size)
ax.set_xticks(x_ticks)
ax.set_yticks(y_ticks)
ax.set_xticklabels(x_labels, fontsize=tick_size)
ax.set_yticklabels(y_ticks, fontsize=tick_size)
ax.set_xlabel('Sequence', fontsize=axis_label_size)
ax.set_xlim(-0.5,q_count-0.5)
ax.set_ylim(0,1)
ax.grid(which='major',axis='y')
# Output module PK by sequence
ax.plot(module_metrics[module]['sequence_pks'])
# Output PK by sequence for each question
for qid in module_metrics[module]['q_pks']:
ax.plot(module_metrics[module]['q_pks'][qid], alpha=0.15, label=qid)
annot = ax.annotate("", xy=(0,0), xytext=(-20,20),textcoords="offset points", bbox=dict(boxstyle="round", fc="w"), arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)
mpl_id = fig.canvas.mpl_connect('motion_notify_event', hover)
Since there are dozens of modules, I created an ipywidgets dropdown to select the module, which then runs a function to output the chart. Nonetheless, whether running it hardcoded as here or from within the function, mpl_connect never seems to fire.
Here's what this one looks like when run

Moving average line is only shown till last ticker.id close

I am working on a moving average indicator which shows the MA line of a given time frame.
For some reason is the MA line only displaced till the last ticker.id period close. So when for example I have set the indicator to show a daily MA the line is only updated when the day closes.
(Link to image https://i.stack.imgur.com/QjkvO.jpg)
Does anyone know how my indicator will be able include data between daily closes, so the line is being updated continuously?
I think this line not being updated continuously also causes the label which is supposed to be plotted right at the MA line being plotted at the 1 point / dollar height on the chart.
I have only recently started writing code, so excuse me if this is a dumb question. I have written this code looking at other indicators and trying to fit parts into my own
This is the code of the entire indicator.
//#version=4
study(title="Custom Timeframe SMA", shorttitle="Custom TF MA", overlay=true)
res = input(title="MA Timeframe", type=input.resolution, defval="D",options=["60", "240", "D", "W"])
length1 = input(title="SMA Length", type=input.integer, defval=50)
Label=input(title="show Labels",defval=true)
sma1 = sma(close, length1)
sourceEmaSmooth1 = security(syminfo.tickerid, res, sma1, barmerge.gaps_on, barmerge.lookahead_on)
plot(sourceEmaSmooth1, style=plot.style_line, linewidth=2, title="25 period", color=#a21e7b)
plotchar((sourceEmaSmooth1 ? Label : barstate.islast and not barstate.isconfirmed) ? sourceEmaSmooth1 : na, location=location.absolute, text=" 50 SMA", textcolor=#a21e7b, offset=10, editable=false)
Using barmerge.gaps_on with security() creates holes which appear as na values at the chart's resolution, which is why your ma wasn't always showing. It wasn't apparent on historical bars because the plot() function fills the space from non-gap to non-gap (you could see it if you plotted circles instead of a line).
Using barmerge.lookahead_on with security() produces lookahead bias on historical bars. Very nasty if you don't index the value you're fetching, as is explained in this publication on how to use security() correctly: How to avoid repainting when using security().
I added show_last = 1 to you label-plotting call and fixed the conditional. Because it now only plots the last occurrence of the label, we no longer need to worry about barstates:
//#version=4
study(title="Custom Timeframe SMA", shorttitle="Custom TF MA", overlay=true)
res = input(title="MA Timeframe", type=input.resolution, defval="D",options=["60", "240", "D", "W"])
length1 = input(title="SMA Length", type=input.integer, defval=50)
Label=input(title="show Labels",defval=true)
sma1 = sma(close, length1)
sourceEmaSmooth1 = security(syminfo.tickerid, res, sma1)
plot(sourceEmaSmooth1, linewidth=2, title="25 period", color=#a21e7b)
plotchar(Label ? sourceEmaSmooth1 : na, location=location.absolute, text=" 50 SMA", textcolor=#a21e7b, offset=10, show_last = 1, editable=false)

Problems appending rows to DataFrame. ZMQ messages to Pandas Dataframe

I am taking messages for market data from a ZMQ subscription and turning it into a pandas dataframe.
I tried creating a empty dataframe and appending rows to it. It did not work out. I keep getting this error.
RuntimeWarning: '<' not supported between instances of 'str' and 'int', sort
order is undefined for incomparable objects
result = result.union(other)
Im guessing this is because Im appending a list of strings to a dataframe. I clear the list then try to append the next row. The data is 9 rows. First one is a string and the other 8 are all floats.
list_heartbeat = []
list_fills= []
market_data_bb = []
market_data_fs = []
abacus_fs = []
abacus_bb =[]
df_bar_data_bb = pd.DataFrame(columns= ['Ticker','Start_Time_Intervl','Interval_Length','Current_Open_Price',
'Previous_Open','Previous_Low','Previous_High','Previous_Close','Message_ID'])
def main():
context = zmq.Context()
socket_sub1 = context.socket(zmq.SUB)
socket_sub2 = context.socket(zmq.SUB)
socket_sub3 = context.socket(zmq.SUB)
print('Opening Socket...')
# We can connect to several endpoints if we desire, and receive from all.
print('Connecting to Nicks BroadCast...')
socket_sub1.connect("Server:port")
socket_sub2.connect("Server:port")
socket_sub3.connect("Server:port")
print('Connected To Nicks BroadCast... Waiting For Messages.')
print('Connected To Jasons Two BroadCasts... Waiting for Messages.')
#socket_sub1.setsockopt_string(zmq.SUBSCRIBE, 'H')
socket_sub1.setsockopt_string(zmq.SUBSCRIBE, 'R')
#socket_sub1.setsockopt_string(zmq.SUBSCRIBE, 'HEARTBEAT') #possible heartbeat from Jason
socket_sub2.setsockopt_string(zmq.SUBSCRIBE, 'BAR_FS')
socket_sub2.setsockopt_string(zmq.SUBSCRIBE, 'HEARTBEAT')
socket_sub2.setsockopt_string(zmq.SUBSCRIBE, 'BAR_BB')
socket_sub3.setsockopt_string(zmq.SUBSCRIBE, 'ABA_FS')
socket_sub3.setsockopt_string(zmq.SUBSCRIBE, 'ABA_BB')
poller = zmq.Poller()
poller.register(socket_sub1, zmq.POLLIN)
poller.register(socket_sub2, zmq.POLLIN)
poller.register(socket_sub3, zmq.POLLIN)
while (running):
try:
socks = dict(poller.poll())
except KeyboardInterrupt:
break
#check if the message is in socks, if so then save to message1-3 for future use.
#Msg1 = heartbeat for Nicks server
#Msg2 = fills
#msg3 Mrkt Data split between FS and BB
#msg4
if socket_sub1 in socks:
message1 = socket_sub1.recv_string()
list_heartbeat.append(message1.split())
if socket_sub2 in socks:
message2 = socket_sub2.recv_string()
message3 = socket_sub2.recv_string()
if message2 == 'HEARTBEAT':
print(message2)
print(message3)
if message2 == 'BAR_BB':
message3_split = message3.split(";")
message3_split = [e[3:] for e in message3_split]
#print(message3_split)
message3_split = message3_split
market_data_bb.append(message3_split)
if len(market_data_bb) > 20:
#df_bar_data_bb = pd.DataFrame(market_data_bb, columns= ['Ticker','Start_Time_Intervl','Interval_Length','Current_Open_Price',
# 'Previous_Open','Previous_Low','Previous_High','Previous_Close','Message_ID'])
#df_bar_data_bb.set_index('Start_Time_Intervl', inplace=True)
#ESA = df_bar_data_bb[df_bar_data_bb['Ticker'] == 'ESA Index'].copy()
#print(ESA)
#df_bar_data_bb.set_index('Start_Time_Intervl', inplace=True)
df_bar_data_bb.append(market_data_bb)
market_data_bb.clear()
print(df_bar_data_bb)
The very bottom is what throws the Error. I found a simple way around this that may or may not work. Its the 4 lines above that create a dataframe then set the index and try to create copies of the dataframe. The only problem is I get about anywhere from 40-90 messages a second and every time I get a new one it creates a new dataframe. I eventually have to create a graph out of this and im not exactly sure how I would create a live graph out of this. But thats another problem.
EDIT: I figured it out. Instead of adding the messages to a list I simply convert each message to a pandas series then call my dataframe globally then do df=df.append(message4,ignore_index=True)
I completely removed the need for lists
if message2 == 'BAR_BB':
message3_split = message3.split(";")
message3_split = [e[3:] for e in message3_split]
message4 = pd.Series(message3_split)
global df_bar_data_bb1
df_bar_data_bb1 = df_bar_data_bb1.append(message4, ignore_index = True)

Psychopy not getting all the answers

I'm using psychopy to build a cognitive task.
I have 5 circles on the screen and the participant needs to pressed on the good circle.
My code :
if mouse.isPressedIn(cercle_1):
continueRoutine = False
# save data if you like:
thisExp.addData('correct', 1)
thisExp.addData('RT', t)
elif mouse.isPressedIn(cercle_2):
# save data if you like:
thisExp.addData('correct', 0)
thisExp.addData('RT', t)
continueRoutine = True
elif mouse.isPressedIn(cercle_3):
# save data if you like:
thisExp.addData('correct', 0)
thisExp.addData('RT', t)
continueRoutine = True
elif mouse.isPressedIn(cercle_4):
# save data if you like:
thisExp.addData('correct', 0)
thisExp.addData('RT', t)
continueRoutine = True
elif mouse.isPressedIn(cercle_5):
# save data if you like:
thisExp.addData('correct', 0)
thisExp.addData('RT', t)
continueRoutine = True
The problem is that my datafile only contains the response time (RT) and the info of the circle_1. I would have no idea if the participant tried other circle before pressing on circle_1.
Question : How can I have in my csv file infos about all the times a participant pressed the mouse bouton.Maybe before pressing cercle_1, he pressed the cercle_3. Right now, I only have how long it took to get the correct answer.
It sounds like you want to record a sequence of events in the trial. This can be hard to find an appropriate data structure for, but in your case, I can think of two solutions.
Record the number of wrong responses
Have a column (e.g. n_wrong) and count the number of non-circle_1 responses. In Begin routine add
n_wrong = 0
Then in each frame add:
if mouse.isPressedIn(cercle_1):
thisExp.addData('correct', 1)
thisExp.addData('RT', t)
continueRoutine = False
elif mouse.isPressedIn(cercle_2) or mouse.isPressedIn(cercle_3) or mouse.isPressedIn(cercle_4) or mouse.isPressedIn(cercle_5):
thisExp.addData('correct', 0)
thisExp.addData('RT', t)
n_wrong += 1 # One more error recorded!
# Now wait until the mouse release to prevent recording 60 wrong clicks per second!
while any(mouse.getPressed()):
pass
Then under end routine add:
thisExp.addData('n_wrong', n_wrong)
Record which circles were pressed
The other is to have a column for each circle and shift those from "unpressed" to "pressed" when they are clicked. Then the column cercle1 would correspond to what you currently call the correct column. So under begin routine:
# Mark all non-target cirlces as unpressed
thisExp.addData('cercle1', 0)
thisExp.addData('cercle2', 0)
thisExp.addData('cercle3', 0)
thisExp.addData('cercle4', 0)
thisExp.addData('cercle5', 0)
Then under each frame I would do this:
if mouse.isPressedIn(cercle_1):
thisExp.addData('cercle1', 1)
continueRoutine = False
if mouse.isPressedIn(cercle_2):
thisExp.addData('cercle2', 1)
if mouse.isPressedIn(cercle_3):
thisExp.addData('cercle3', 1)
if mouse.isPressedIn(cercle_4):
thisExp.addData('cercle4', 1)
if mouse.isPressedIn(cercle_5):
thisExp.addData('cercle5', 1)
The latter approach could be extended with reaction times by adding columns called cercle1_rt etc. but then you'd also need to do the while any(mouse.getPressed()): pass trick to record the onset and not just the release.