Skip to content

Latest commit

 

History

History
156 lines (114 loc) · 5.8 KB

240313.services.md

File metadata and controls

156 lines (114 loc) · 5.8 KB

https://discordapp.com/channels/918498540232253480/1217407920430645329/1217407920430645329

Sedmund OP — 03/13/2024 2: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. React to Post

Follow

kasperl — 03/13/2024 3:46 AM Thanks for posting! I'll take a look and try to cook up an answer for you within an hour or so [3: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. [3:50 AM] I assume you want to call get_sensorValue across the container boundary.

kasperl — 03/13/2024 4: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 [4: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 — 03/13/2024 4: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). Gist M5LoRaWAN - TOIT M5LoRaWAN - TOIT. GitHub Gist: instantly share code, notes, and snippets.

kasperl — 03/13/2024 4: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 OP — 03/13/2024 4: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?

kasperl — 03/13/2024 5:45 AM Yes, definitely.