❔ Making an async function or event system to get steps on an encoder.
I am using an encoder with the GPIO controller on my pi in C#, the main issue is I dont understand how to read the leading edges without making a constantly running function. I have ha 2 ideas of how to read the encoder in and its either an event to increase a counter, or an async function that is constantly running to look for the leading edge. the issue is im not particularly versed in either of those things.
we are using this library: https://learn.microsoft.com/en-us/dotnet/api/system.device.gpio.gpiocontroller?source=recommendations&view=iot-dotnet-latest
it has a async function for reading but I dont know how to use that to count without calling it every tick or something
GpioController Class (System.Device.Gpio)
Represents a general-purpose I/O (GPIO) controller.
266 Replies
I feel like GPIO isn't really an interface intended to do... that
then what are you supposed to do. I need to control the motor with the pi, what are my options. The pi can see when a pin changes, could I not make an event to increment a counter?
the most proper way to do something like this is with interrupts
the encoder is 2700 pulses per rotation, and the motor spins at 60 rpm, so its a maximum of 2700 hz the code should be able to keep up
I.E. events
barring that, you're just gonna have to implement your own sampling loop
this is a PWM motor speed feedback?
if you've gotta make your own loop, you're definitely gonna want a timer
well its a dc motor controlled by pwm but it has a duel channel encoder on its output
thats what we are trying to read
so I just have 2 pins on read mode and i can read in from them
dual channel meaning differential between the two?
one is rising edge one is falling edge, you can tell the direction based on which goes high first
ahhh, okay
anyway, yeah, if the GPIO interface doesn't provide events or callbacks for you, you'll need a sampling loop, based on a timer
either
System.Timers.Timer
or System.Threading.Timer
I'm rusty on what the differences are, there's like 5 different Timer
APIs just in the BCL aloneok so am i making like a tick event and just every tick read it?
that's pretty much how a Timer works, yeah
so this is more like just reading it every chance u can then. not really an event system
and don't count on the Timer being accurate, bias all your readings with timestamps
right
sampling
then you can generate your OWN event, if needed, for your other code
ok so the async functions it provides are useless
maybe?
what async functions?
WaitForEvent(Int32, PinEventTypes, TimeSpan)
well, that doesn't sound useless
yeah i didnt think it was just didnt know how to use it
standby, lemme move to my desktop
and link me the docs for that
GpioController Class (System.Device.Gpio)
Represents a general-purpose I/O (GPIO) controller.
so, uhh, yeah
I would not call this an async function
maybe
but, uhhh
it do be havin' that
PinEventTypes
enumit do
ahh, yeah, here we go
you want
.WaitForEventAsync()
presumably
and, I mean, the docs specify pretty cleanly how to use ityeah, i said there was an async function, i just copied the wrong one. but dont you have to re call it every time it finishes
you call it and it will wait for an event to occur, as you specify
in the case of the
"Async"
version, it will wait asynchronously
in that it will not wait, it will return a Task
that will complete when the event occurs
which you can await
so, for practical usage, in your scenario
you'll want to setup an async loop
something to the effect ofI thought it would be a constantly running function that every time it got a read a high it would re call that function to look for another. and just increment a counter that can be called whenever
just have no idea how to do that
or maybe something like
so as long as the token isnt reached it will keep calling the even function.
it's a loop
it will loop until you tell it not to loop
and with this if i output currentStep it will give me the current step
uhh
i just call this function once at the start of the program and that task will stay running?
I'll assume you meant to put that in proper English and say "yes"
yes
the top level of your program will probably look something like
better yet
you'll use Microsoft's already-engineered solution for this kinda thing
.NET Generic Host - .NET
Learn about the .NET Generic Host, which is responsible for app startup and lifetime management.
long story short
then in my main how to i get my
currentStep
valueDI
I.E.
you don't do it in Main()
Main()
is just for orchestrating all the different things that run in parallelcan you not just run the main body on he main thread?
what are you ultimately trying to do with
CurrentStep
you canwell im using it for position values
you can code whatever you feel like coding for
i need to know what direction my motor is facing
so i need to know my step count in the body of the code to know how much to turn it
the point is that when you have complex things interacting, you should use the tools that are made for you to do those complex things
like have multiple separate logical things running in parallel
yes the issue is im on a pi, and the number of threads i have is 4
exactly
that's why you're using
async
/await
the number of physical threads you have available is irrelevant
you get logical parallelism, regardless of hardwarewell if i queue 5 tasks, one isnt running and if its the encoder one i am losing data and my position am i not?
if you're properly leveraging
async
/await
, "queue 5 tasks" tells you nothing about how many physical threads you're using
so, like
context
what is this all forI'm in a rocket team and this is for a steerable parachute system, we need to know how far the motors have turned because that is giving the angle of the parachute
k
so
here's the thing
your program is going to spend 99% of its time doing literally nothing
so we need to read gps data, imu data, bmp data, calculate our heading 10 times a second and move the motor
right
as fast as we can ideally
your goal is to write a program that sits idle, most of the time, waiting for input from one of these sources
when a batch of data comes in from the GPS unit
or the IMU
or the motor controller
you spend a small amount of time processing that input
then go back to being idle, waiting for the next one
well 4 things are going to be constantly happening. there is almost no down time. the motor will be spinning constantly to keep up with the rotation of the physical system, and the encoders will be changing at a rate of 45 times a second
and your CPU runs about 10^6 times faster than that
from the point of view of your CPU/program, the majority of your time is spent doing nothing
i get that, just wasnt sure since the 2 encoder tasks are permanently running if that counts ad essentially -2 threads
since its awaiting for the next rising edge i didnt know if anything else would run on those threads
I gotta say, you REALLY lucked out on who you got to pick up this thread
not only am I actually an electronics engineer, as opposed to just a computer scientist or web developer, like most folks here
yeah im not the best with threads so im glad you know this
I haven't been active around here for like a year, until just the past week or so
so, I'm gonna go ahead and sidetrack for a moment to talk about how you should be handling these I/O devices
let's take the motor controller
you mentioned the motor spins at 2700 RPM earlier?
or that the encoder can spit out a signal at 2700Hz maximum?
no it spins at 60 rpm
the encoder is 2700 pulses per rotation
k
so, your maximum signal rate is 2700*60
well, really
probably double that?
what's the width of these pulses?
There are ~2700 pulses per rotation and the motor can only output 60 RPM so the math works out to ~2700 pulses per second AKA 2700Hz
right, right, RPM not RPS
but still, what's the pulse width
With a cpu clock of 1.5GHz and the nyquist theorem, the Pi should be fast enough right? I don't know the contact arc length on the encoder
at 2700Hz, pulse delay would be 37ms
aha
you're already where I'm trying to go
No it would be 0.37ms right?
it's worth verifying it specifically, but yeah, you're looking for the sampling frequency of the GPIO controller to be at least double the switching frequency of the signal
I dont know about the hardware of the Pi. So who knows if the sample rate is anywhere near the cpu clock
it's 3.7e-4, so... fuck, stop making my brain look bad
that's actually 370us, then, yeah?
yes
yeah, okay
so, I dunno if that's something that's controllable with the GPIO interface
like I said earlier, the ideal setup is that
.WaitForEventAsync()
is actually listening for an interrupt
I.E. it's not sampling at all, it's sampling in hardware
you definitely don't want the GPIO controller sampling "constantly"
you want it sampling, at most, at 2x the switching frequency, or ideally not at all
same goes for all your other data sources
they're either sending you data, via interrupts
or you're setting up a sampling loop in software, to sample at some SPECIFIC frequency, depending on the data source
all that comes together to form a program that spends the vast majority of its time idleill look more into that host builder, i still dont know how you are passing data from service to service
through the DI system
Microsoft.Extensions.DependencyInjection
oh ok
example
your
MotorControllerService
might look like thisalso for an
await
is it not suspending that thread untill it gets an answer?that is pretty much what is happening, yes
except
it's not suspending a thread
it's suspending a logical thread
which is even better
if you're properly applying
async
/await
what you're actually doing is delegating the entirety of your program to the ThreadPool
every time something triggers your code
like, when a GPIO event occurs
what actually happens is that your code gets sent to the ThreadPool
to be scheduledok ill be back in like 5 i gotta drive home real quick. I have to switch accounts this in one setup on the computer not mine @FacePalmGamer is my actual account
the
ThreadPool
picks out an available thread to run your code
and your code runs on that thread, until the next time an await
makes it suspend
at which point the physical thread goes back to the ThreadPool
so, you could have any number of "logical" threads running all at once
say, one for every one of your data peripherals
and then maybe another for your main control loop
maybe that's 6 "logical" threads
but they're not all "physically" running at once
they spend most of their time suspended
whenever something triggers one of them to resume, it gets ASSIGNED to a physical thread
instead of each one taking up its own physical thread
with traditional threading, each one of these would be its own Thread
object, running on a physical OS-managed thread
and when one needs to resume, it sends a signal to the OS to give it some CPU time
which requires assigning that thread to a core, to execute
or waiting for a free core
I mean, it all sounds VEEEEEERRY similar to what I just described for "logical" threads, because it is
but at the end of the day, a "logical" thread in .NET land is WAY more lightweight than a physical thread, managed by the OS
so, it's a decent performance boost, especially if you're running on something like a PI with resource constraints
or where your application is quite time-sensitive, which yours is
so
example
BackgroundService
is a base class that comes with the hosting framework
it's a half-implementation of IHostedService
, which is the real magicoh ok, then the only question is when the logic thread is suspended if its not on a physical thread how can it ever be notified that it got what it wanted?
IHostedService
is
and when you do IHost.Run()
the host searches for all registered IHostedService
classes, and calls StartAsync()
on them
and then will call StopAsync()
later, if instructed to do so
in this way, you can setup a variety of different "background" tasks that are constantly "running"
on top of that
if you register MotorController
the right way, with the IoC container
other services can reference it
so, for a program like this, you probably want some central "control" loop
and that can be its own BackgroundService
yes this is what i would refer to as my main body
and then loop over that
so, you would setup that class to have
MotorControllerService
as a "dependency"ok so i wont have any "meat" in my main. it will just be build this service manager and thats it
and within the
ExecuteAsync()
method of that class, you would do
or something to that effect
right, the interesting stuff would be going on within its own BackgroundService
class
maybe PrimaryControlLoopService
or similarok I never knew about building an app like this
this is really interesting
little bit
you certainly don't HAVE to
but boy it beats trying to micro-manage all this kinda stuff within one master
Main()
method
in my mind
and yeah, I keep going back to that MotorControllerService
class because you can make your life a lot easier with it
you keep saying you need to know the motor's position, but... do you?well almost all data from the sensor, i will just call and ask for it when needed, but the encoder is different since it has to update constantly so i didnt know how to achieve that effect
is that what you REALLY care about?
or do you care about using the position, incrementally, to figure out the speed?
is the motor constantly spinning?
yes, i need to know the motor a is at 75% and motor b is at 75% so that my heading is 45 degrees north east
or is it more like a servo?
but as the physical payload is spinning those values will change to match the rotation so that my flight heading remains at 45 degrees north east
so the motors position will follow a sin and cos wave to keep the heading constant
is this a gyro?
and the encoder verifies where it is at. i don't care about the speed at all, just the ratio of the motors
there is a gyro on board on the imu so we have a heading to get our rotation that we need to counteract
I can't fully picture it, but alright
whatever value you're interested in
whatever value is meaningful for your navigation logic
that's the value you expose on
MotorControllerService
so that NavigationService
(let's call it that now, that's better)imagine a falling brick with a parachute in the shape of a + each side has a wire. if you pull in the left side and release the right side, you will go directly left.
can poll that value whenever it wants
so, the motors are essentially servos, controlling flaps
yes
lovely
we are using these as VERY BEEFY servos
yeah
the reason you want a
MotorConrollerService
is that the process of reading feedback from the motors is asynchronous, compared to how often your navigation loop needs to poll for motor position
if any of your other sensors operate that way, you'll want a ControllerService
for them as well
and then your main NavigationService
will be a loop that runs at a particular update frequency
yes, you want it to run as fast as possible, but not LITERALLY
you'll want to set the update frequency based on the response times of your sensors, and your motor controller outputs (and any other outputs)do you set the update time of each service individually?
like, there's a point where running more-frequent updates doesn't get you any practical value, because the sensors and the motors can't respond any faster
so, if you run updates faster than that, you're just wasting power
from the sound of it, the only service that'll explicitly be a timed update loop, is the main navigation
the motor controller is a loop, but it isn't timed, it's interrupt-triggered
and the other sensors are either not interrupt-based, in which case, they likely don't need a separate service, or also interrupt-triggered, thus also not-timed
I call them with i2C when i want a value
yeah
you're explicitly polling them
yup
cause they run their own sampling/polling loops, in hardware
or firmware
the motor controllers don't
they can't be polled
so, you need a background service to MAKE them pollable
the only other concern might be that if any of the other sensors take a particularly LONG time to pool, you might want a separate service for that polling, just to help keep the navigation loop from bogging down
I.E. that would let you, say, run the navigation loop 3 times faster than the GPS polling loop
allowing the navigation to take advantage of the faster speed of all the other sensors
the way i was doing the motor before when we were using steppers was just
and that got me the turn i wanted.
with these since they are dc it should be closer to
dont ask why we switched form stepper to dc, long story
would i want that in a service or just the main service
personally, I would probably put all that outside of the main loop
cause it's a closed-loop system
you have one input to that loop, which is effectively a target heading
yeah?
yes
and i might need to change that before it reaches it
if i am able to update the heading faster than i reach the location I would
so, you can setup that loop to just have a target position or target heading input
percentage is best. the equation spits out a percentage of each motor i need (so the ratio)
and because its speed is limited purely by the responses from the encoder, it can run basically as efficiently as possible
as soon as the encoder event comes in, you can calculate the new PWM value to output
and you then have the freedom to make that feeback more sophisticated
without affecting the navigation logic
like, make it a full PID loop, instead of just a P loop
the navigation loop wouldn't care
now
maybe that doesn't make sense
how much do the motor outputs depend on feedback from, say, the GPS or IMU?
maybe the motor loop isn't as "closed" as I describe it
IMU is what we care about. GPS is for initial heading, after that imu is trying to keep us there
if so, maybe just have the motor controller just publish the position value from the most-recent input from the encoder
and navigation can do the rest
as IMU says hey we have spun 15 degrees (because falling objects spin) we have to counteract that with motors
yeah
like
okay
so
we established earlier we're using these motors as glorified servos
but, they're actually not, right?
the position is not controlled in hardware, you're controlling it in software, right?
the PWM output is "target motor speed" essentially, yeah?
so just so i understand this ima going to write some psudo code
i can link them
we are using them like servos thouhg
right, but
you're making that happen in software?
pwm only controls speed. there is almost no reason not to put it at full throttle until your encoder says stop
a constant PWM signal does not equate to constant motor position?
right
okay
so, 100% I think
MotorControllerService
should be the service that contains that secondary control loopthe only reason pwm is anything but full forward or full reverse is so it doesnt over heat/ more precision
yeah
your nav loop decides what the motor position OUGHT to be
and the motor loop adjusts speed back and forth in its best attempt to get there
and yeah, that sounds like a perfect use-case for a PID loop
and it definitely benefits you to have that loop run as responsively as possible
I.E. realtime
it doesn't run on a timer, it runs the moment any input is received
either input from the encoder, or input from the nav loop
all of those inputs, plus information from the previous loop, go together to determine the desired power setting for the motor
ok so i am correct in assuming i will have 3 tasks
from what you describe, you'll have that, and you'll have the main loop
I would say 2
you don't really gain anything by having encoder-reading and motor spin as separate
your motor spin logic can be completely reactive
(and iterative, I suppose)
and your goal is to call that function whenever an encoder signal comes in (I.E.
currentMotorPosition
changes) or targetMotorPosition
is changed by the main loop
fully-reactive
cause either your output power to the motor is non-zero
meaning the motor will move
and new encoder data will come in momentarily
or you reach your target position, and the motor output is 0
and nothing triggers this functionok i see, so it will look something like
and then i would just edit double percent via the DI thinkg u mention in the main loop
not until the main loop changes
targetMotorPosition
again
kinda
obviously, you're missing the part where you change the PWM outputwell the pwm doesnt really need to change
how so?
either its full forward or full reversed or stopped
so
it does need to change
yeah it would just be if the desired encoder value is less then current change
is forward/reverse/off controlled by some other signal?
yeah
or greater
and I would definitely thing that scaling the motor dutycycle would be a good idea
right
so, like
yeah i am trying to dumb it down as much as possible for now, as long as i have a good direction i will make it more proper when i get there
absolutely
yes
enhancing the control loop to scale the motor power would definitely fall under "tuning" of the control loop
so is the service just constantly running then im using DI to edit the percent
or is there something im missing that i didnt understand
constantly running, logically
yes
physically, it's sitting mostly idle
mostly idle in the thread pool
physically, it only responds to specific events
either an encoder signal, or a change from the main loop
in that regard, your loop code above is flawed
it doesn't actually "respond" to changes from the main loop
it only responds to encoder input
if you tuned that loop well enough, so that it got the motors to the exact right positon, and the encoders stopped giving you input
the whole thing would freeze
if the main loop set a new target, the motor loop would never try to reach it
cause it's still waiting on a signal from the encoder
which will never come
(I mean, realistically, turbulance will bump it around, but yeah)
oh so how do i make an interrupt from the main loop change the desired value of the motor position
a couple ways
you can either not use
.WaitForEventAsync()
(which doesn't mean you lose all the advantage of the ThreadPool
stuff, you're just gonna do it a bit differently)
you'd use (it looks like)
RegisterCallbackForPinValueChangedEvent
well hold on
before you start typing a paragraph
hehe
with the current system. we are awaiting 1 pin
k
there are 2 pins letting you know direction. is it possible to wait for a few things at once
for the first one to respond
yeah, that's what we're heading towards
gotcha
and actually, it's 4, right?
2 per motor?
yeah but each motor would be its own service correct?
well, maybe
so i will have 3 services (2 are for motors, since i have 2 motors)
the main loop will do all the logic
each motor loop controls that motor and looks for encoder signal
you'd want them to be 2 instances of the same class
that way the main loop can just say "hey motor 1 try to go here, motor 2 try here"
yeah
yeah just different input values
the problem is that the DI system isn't quite setup for that
it's a little tricky
I suppose you COULD do
i could just copy paste the class and give them slightly different names
nah
that's smelly
and then your main nav service would have to do
something like that
each controller would have to self-identify which motor it's controlling
cause the DI system will only give you a collection of all of them that've been registered
but that should work
ok so you identify each one on startup and make a local variable in the main loop service to call later
yeah
ok then now back to the motor service. how do you awat 3 things and do something differently for each
well, in this case, you don't REALLY want to do something differently
like
sort of
but also kinda not
so
the older-fashioned way would be to just use event handlers
callbacks
looking at
GpioController
, it has RegisterCallbackForPinValueChangedEvent()
which is identical to WaitForEvent()
and WaitForEventAsync()
except yet another mechanism for response
you GIVE it a function reference to be called
instead of getting a Task
to be await
edyeah I have used events for winforms, drawing on tick, as well as unity for various things
yup
same thing
and your "event" for when the main loop makes an update is just.... the method that the main loop is already calling
the only thing you gotta worry about is thread-safety
since the main loop, and the callbacks can potentially be running simultaenously on different threads
the more async-y way to do it would be to use a
Channel
so it would be
for each pin, and then how do u do the main changing the desired percentage?
and your "event" for when the main loop makes an update is just.... the method that the main loop is already callingso as soon as the main loop sets
TargetMotorPosition
, UpdateMotorOutput()
gets calledyeah i see that
it also gets called as soon as a pin value changes, after a quick re-calculation of what
CurrentMotorPosition
is
or however you want to implement that bithow does the main loop set targetmotorposition
that's the, uhhh
explicit way to do it
is this where the DI thing u mention comes in
yes
you register all of your different classes and services and whatever with the IoC (Inversion of Control) container
when the host starts up, it uses dependency injection to request all registered
IHostedService
objects from the container, and starts themok then can you just set that value like its a property?
since
NavigationService
would be one of them
and NavigationService
has a constructor that requests IEnumerable<MotorControllerService>
as a dependency
that dependency gets "injected" by the IoC container, when it constructs the instances of that class
which the main host requested
the IoC container "injects" all registered MotorControllerService
objects into NavigationService
, via the constructor
and then it'd be NavigationService
's responsibility to identify which controller belongs to which motor
and then, yeah
it's just a reference to a class like any otherDifferent person here from before who referenced the nyquist theorem. This is not a servo because we dont know the absolute position of the output shaft. The only accurate description is what is actually being used. A DC motor with a gear reduction and an encoder. We'll be using limit switches to determine when we have reached the start/end of the spool of parachute cable. This is I guess more akin to a stepper in which you never know the true position of the output shaft but know how many radians it has turned since you started moving it.
ok so this is the event way to do it. is there a proper way to do it with
await
and actually await on both pins or something from the main loopwait
what the fuck
when did you change people
this is a team thing, we are the programmers if that wasnt clear. it was his laptop i was using
I completely missed that
we both went home
I mean, I heard you say it was a team thing
The art of misdirection lol
completely missed that I was talking to two people
well played
lol
anyways ima re ping that
so, uhh
i get this gets the same effective result, but knowing how to await multiple things would be nice
yeah
there's a couple ways to do it
you can do
Task.WhenAny()
when you call .WaitForEventAsync()
it returns a Task
which you don't HAVE to await
right away
if you have another thing you might want to await
, you can call that, and get it's Task
and then do a combination await
on both of them
or any number of Task
s
Task.WhenAll()
accepts any number of input Task
s, and return a Task
that will completed when all of the inputs have completed
Task.WhenAny()
is the same, except its result Task
will complete when ANY of the inputs have completedI have used when all actually with a snake AI, just running a bunch of snake generations. forgot about whenany
so, you could build a
Task
that you trigger to complete, whenever the main loop makes an updatehow do i get a task from the main to give me back a new percent value
you'd use a
TaskCompletionSource
most likely
but
this is messy in a couple ways
first, after await Task.WhenAny()
completes, you then need to go BACK and check which of the two tasks completed
then extract the result and loop back and make sure you pass the same task that didn't complete to Task.WhenAny()
along with a new Task
to replace the one that did completewell in the case that the main class changed the value that we want to achieve, the loop variable will be updated and we go back to waiting for encoders and or the main to update again
it's also messy in that it doesn't really allow for multiple-triggerings from the main loop
correct, I'm just saying the code to do all that correctly is messy
and also not QUITE foolproof, if you're just using a
TaskCompletionSource
ah i see
so the event method seems to be the clean way to do this
the much cleaner thing to do, if you want an
async
/await
solution
is use a Channel
oh
yeah u mentioned that
havent heard of that one
it's rather niche
but it's SUCH a cool API
call me a nerd, but it's one of my favorite APIs in .NET
so is this just a line of constantly changing data that can be read from?
essentially a data stream you can read write to and have multiple members interacting with it
yeah, logically
cool, but how does this help the await system
i see you could put the encoder value and the desired motor position on it, this way the main loop could just edit the channel and the motor position loop would update
but you would still have to
waitany
for either channel a or channel b on the motor
unless there is more to thisyou can be a little cleverer, with channels
possibly a bit over-engineered, but yeah
now that I write it out, like
Channels are cool
but one or two
SempahoreSlim
s would probably be more appropriate hereok humor me. why does everyone use var. that is the dumbest thing to me. like its why python is an abomination. i want to know what type my variables are what is the point of making
var x = 0.0d
why not just double x = 0
personally
because I mostly don't give a shit what the types are
i get using it in a pinch because of long datatypes but like for the double cmon
the vast majority of the time, the types are right, or shit doesn't compile
the much better approach, IMO, is for the code to be self-descriptive
var input = _motorInputs.ReadAllAsync()
I can see at a glance that input
is some kinda... motor input
what specifically?
I don't care
if I DO care, I'll go look at the definition of that type
I don't need the details of the definition of that type cluttering up the readability of this line of code
same goes for currentMotorPosition
and targetMotorPosition
as far as readability goes, what the specific type of those variables is is irrelevant
I care that I can perform operations on them
if I want to know specifically, either for debugging or sanity checking, I can hover and make surei can see where u are coming form, but personally i will never use it
and that's valid
just was never sure if it was an industry thing or just a personal decision
little of both
for me, it's probably also a function of me writing a LOT of JavaScript and TypeScript for work, alongside .NET
anyways back to this
where not only do I not WANT to know the type, half the time, I CAN'T know the type, for
kek big hater of webdesign JS is terrible
i cant do it
agreed
anyways
in the code the channel is a read only, how does it get changed?
there's two spots where it's being written to
_motorInputs.Writer.WriteAsync()
which is exactly what we discussed
the encoder listener writes to itah
SetTargetMotorPositionAsync
and ListenToEncoderAsync
and the caller from the main loop, yeah
i see that now
MotorInput
is assumed to be like a private struct
you could write
along with a MotorInputType
enumwhat is calling the encoder function in this case
ListenToEncoderAsync()
?yeah
ExecuteAsync()
is it the
ececuteasync
ok
so that is the service caller it constantly runs that correct?logically, yes
it's
async
, so whenever it hits an await
for a Task
that's incomplete, the method suspends
as discussed earlierpublic Task SetTargetMotorPositionAsync(double targetMotorPosition) => _motorInputs.Writer.WriteAsync(MotorInput.TargetPositionChanged(targetMotorPosition));
is there any reason to make this async?yes
its just setting a double
it's doing it thread-safely
it makes the caller wait if the channel is currently in the middle of being read from, for example
and whenever you want something to wait, you want
async
in reality, that's an await
that will virtually always complete immediately, and thus not require a suspension
likebut the writer is writing async and you arent awaiting it. so are you just awaiting it in the main loop?
reading from the channel causes it to momentarily lock
as would writing to it
well, yeah
I don't need to await it if I'm just returning the
Task
directly
the await
here is redundant
functionally equivalentwasnt sure if you even needed to await it or return the task, because main doesnt really care if that is done as long as it happens
the main loop would await
SetTargetPositionAsync()
regardless
unless the main loop also has the opportunity to elide it
which it won'tyeah, but you could just make it iterative and just have that line. its gonna be called then immediately awaited
because the main loop is a loop, and thus has both pre-await and post-await actions to take
an
await
can be elided if it's the only one within a method, and there are no post-await actions that need to be taken
I.E. the method doesn't actually care about the completion of the Task
alright. I have the event method, and the channel await method
I will look more into doing this DI thing, and setting up the service app builder
thanks for all the info its really helpful
read the Microsoft docs for the hosting platform
and for DI
was planning on it
.NET Generic Host - .NET
Learn about the .NET Generic Host, which is responsible for app startup and lifetime management.
Dependency injection - .NET
Learn how to use dependency injection within your .NET apps. Discover how to registration services, define service lifetimes, and express dependencies in C#.
and if you don't feel like using these libs for this project, feel free not to
they're just tools
useful tools, but if you're not comfortable, if you just wanna focus on the project with what you already know
like
nothing wrong with that
i mean after you basically gave me the motor service i just need to figure out how to do the injections then build it, and building it doesn't seem to be hard
i got a month to get this working
plenty of time
lol
o7
i also have to write a library for an IMU but thats a different problem
never written one before but got some info on it yesterday from some people in #help-0 using the binary reader writer and primitive library, so just gotta learn those as well and write a bunch of registries and decode data
couldn't possibly be that hard (is what i will keep telling myself until its done)
sounds fun, actually
I haven't done low-level stuff like that in a long time
would be interesting to do in C#
i mean i want to learn how to do it for sure, but i have like almost no place to start
there is already a library for it in python and C so i was gonna direct convert it 1:1 from C and got told no the other day
so now im starting from scratch making it myself
like, someone here said that?
in the help chat yeah
like, you were gonna just take the C code and convert it to C#
yeah, lol, not quite so simple
because i was asking about an equivalent to Unions from C
loooooooooooool
yeah pointers are dumb
the most effective bet would probably be to P/Invoke into the C library
which is a bit outside my expertise
I only really know how that stuff works in theory
i will keep that in mind as a possible solution if im out of time
i have an older sensor i can throw in if i run out, but ideally i get this working
but in theory, you should just be able to load the DLL compiled from C, and call methods within it
I have 3 people on my team helping me, so ill probably hand the motor off to someone and focus more on that since now i can point them in a direction and can reference this chat
well, good luck
tyvm, and thanks again for all the info
Was this issue resolved? If so, run
/close
- otherwise I will mark this as stale and this post will be archived until there is new activity.
Was this issue resolved? If so, run /close
- otherwise I will mark this as stale and this post will be archived until there is new activity.
Was this issue resolved? If so, run /close
- otherwise I will mark this as stale and this post will be archived until there is new activity.