guild icon
Toit
#Implement services for communication in preexisting project
Thread channel in help
Sedmund
Sedmund 03/13/2024 09:44 AM
Hello!

Me and my friend are working on a project where we measure distance using an ultrasonic sensor and send this data to The Things Network. We send the data using a M5LoRaWAN868 external Lora module and we want to implement a way to establish communication between the LoRa module container and the sensor container. For example, at this moment we have a main file that imports both the LoRa module class and the sensor class and then runs them in tasks. We can then retrieve the sensor values and send them by using the main file as a mediator between them. The main file also contains a deep sleep call, which auto runs the installed containers again when it wakes up.

This method we have used above (simply running the different classes in tasks) works but they are not running simultaneously.

We assume it would be almost the same when working with services, however with the communication working between the installed containers. We've been reading the services documentation but is there any examples that shows the containers running for example while true loops?

For example in the code below, which we use to test the ultrasonic sensor:

import dyp_a01 show DYP_A01 import gpio class sensorModule: sensorValue := 0 sensor := null constructor tx_/int rx_/int: sensor = DYP_A01 // --tx_pin=tx not used --rx_pin=rx_ start: task::sensor_read sensor_read: print "Sensor reading started." while true: sensorValue = sensor.range sensor.off get_sensorValue -> int: return sensorValue

and then we start the reading in the main file:

sensorModule := sensorModule sensor_tx sensor_rx loraModule.start sensorModule.start

We would want to have these running independently without blocking one another as they could do with tasks, if the Port.read waits for data. How would we go about implementing services to these classes? For example the one above.
bitphlipphar
bitphlipphar 03/13/2024 10:46 AM
Thanks for posting! I'll take a look and try to cook up an answer for you within an hour or so :🙂:
bitphlipphar
bitphlipphar 03/13/2024 10:48 AM
As a starting point, notice how we use spawn to start a separate running container to run the service here: https://github.com/toitlang/toit/blob/master/examples/service/random.toit#L29.
bitphlipphar
bitphlipphar 03/13/2024 10:50 AM
I assume you want to call get_sensorValue across the container boundary.
bitphlipphar
bitphlipphar 03/13/2024 11:02 AM
import gpio import system.services import dyp_a01 show DYP_A01 main: spawn:: service := RangeSensorServiceProvider service.install task:: service.run --rx=17 client := RangeSensorServiceClient while true: print "range = $client.range mm" sleep --ms=1_000 // ------------------------------------------------------------------ interface RangeSensorService: static SELECTOR ::= services.ServiceSelector --uuid="36989d64-baad-4f06-90A8-fcf796a287c1" --major=1 --minor=0 range -> int static RANGE-INDEX ::= 0 // ------------------------------------------------------------------ class RangeSensorServiceClient extends services.ServiceClient implements RangeSensorService: static SELECTOR ::= RangeSensorService.SELECTOR constructor selector/services.ServiceSelector=SELECTOR: assert: selector.matches SELECTOR super selector range -> int: return invoke_ RangeSensorService.RANGE-INDEX null // ------------------------------------------------------------------ class RangeSensorServiceProvider extends services.ServiceProvider implements RangeSensorService services.ServiceHandler: range-last_ := 0 constructor: super "range-sensor" --major=1 --minor=0 provides RangeSensorService.SELECTOR --handler=this handle index/int arguments/any --gid/int --client/int -> any: if index == RangeSensorService.RANGE-INDEX: return range unreachable range -> int: return range-last_ run --rx/int -> none: sensor := DYP_A01 // --tx-pin=tx not used --rx-pin=rx while true: range-last_ = sensor.range sensor.off
👍1
bitphlipphar
bitphlipphar 03/13/2024 11:02 AM
I haven't tried this and there are a few odd choices (do you really want to read continously from the sensor in a while loop? do you not care that sensor.off is never called?), but maybe this is a good start for you.(edited)
ApEf
ApEf 03/13/2024 11:28 AM
Hello!
I am also working on this project together with Sedmund and first of all, thanks for your answer!

That seems to be what we want to do with the sensor, and yes we will remove the loop (it was there for testing). There are some odd choices in the other class files too that will be removed when the initial testing has been done.

However when looking at the M5LoRaWAN868 class we wrote to communicate with the external device (https://gist.github.com/apef/0ee758a2b38fde1faf476f66680303c7 <- very messy atm), can we implement it in the same way as the "RangeSensorService" above? We have a bunch of functions that are called from within the run function. Should they also be defined the same way as the run function above?

Some functions are not needed, for example reading lora and printing it in the terminal will not be used in the future (as the device is supposed to be installed in a stormdrain).
M5LoRaWAN - TOIT. GitHub Gist: instantly share code, notes, and snippets.
bitphlipphar
bitphlipphar 03/13/2024 11:31 AM
You can add any number of methods on the provider class, so refactor away :🙂: The methods that needs special handling are the ones that can be called across the container boundary using the invoke_/handle methods on the client and provider.
Sedmund
Sedmund 03/13/2024 11:36 AM
Thanks for your answer! Does this mean that we can refactor out the provider class into a separate file and then import it?
bitphlipphar
bitphlipphar 03/13/2024 12:45 PM
Yes, definitely.
10 messages in total