toitlang / jaguar

Use live reloading over WiFI to turbo-charge developing for your ESP32.
MIT License
180 stars 13 forks source link

Support providing runtime-accessible configurations as part of flashing #92

Closed lutzbickhardt closed 2 years ago

lutzbickhardt commented 2 years ago

(this might impact the toit language as such not only jaguar)

Imagine you want to write (more or less) portable code for different versions of hardware (such as M5stackCore2 or M5StackBasic) with different sensors built in. Those sensors might even perform the same functions but are programmed differently. Rather than writing a separate program for each configuration one might identify the device at runtime and act accordingly (think: switch (hw) or #ifdef hw1 in c/c++).

It would thus be beneficial to "know" what particular device a program runs on. Name and ip address are configurable/change, USB serial number is fix.

(By the way: can I access ip and name in toit as supplied by flash?)

Since the USB device description is not available inside the esp32 itself (since this is handled by the CP... which can only be accessed from outside) it would be helpful to supply this when "jag flash"ing (like the wifi credentials) AND make it available from inside a toit program (or write it to EEPROM, but does toit support EEPROM?). Candidates are serial number (on a Mac /dev/ttyXXXXXX) and maybe vendor/manufacturer/product id as given by the USB device descriptor (lsusb -v in Linux).

Thinking this a little further, could we supply a "configuration key" by using "flash" (or any other means) which can be used at runtime in a toit program for such config purposes?

kasperl commented 2 years ago

Thanks, @lutzbickhardt! This is a good idea. The WiFi credentials are stored in the flashed image using ubjson, so it should be possible to extend this with more keys and values quite easily.

The code that creates the ubjson blob uses this struct: https://github.com/toitlang/jaguar/blob/4f711a8fac8cb8f2c7a7aeac2948bb165402ac3c/cmd/jag/commands/flash.go#L21

and it is accessed from the device here: https://github.com/toitlang/jaguar/blob/4f711a8fac8cb8f2c7a7aeac2948bb165402ac3c/src/jaguar.toit#L34 https://github.com/toitlang/jaguar/blob/4f711a8fac8cb8f2c7a7aeac2948bb165402ac3c/src/jaguar.toit#L48

lutzbickhardt commented 2 years ago

hmm, I was trying to build jaguar for myself and followed the instructions ... [Ubuntu 20.04]

kasperl commented 2 years ago

@lutzbickhardt: Looks like you might need to run toit.pkg install in /home/lutz/jaguar to fetch dependencies.

lutzbickhardt commented 2 years ago

well, I updated to golang to 1.16 (not from apt but with wget), ran again according to the recently updated instructions to build toit and then continued with the instructions to build jaguar and it WORKED. I changed some text in jaguar/cmd/jag/commands/flash.go, rebuilt jag and voila! thanks for the tips!

getting more adventurous I tried to include the serial number along with the wifi credentials. There is a libusb module (https://github.com/gotmc/libusb) with some examples and a code snipped that should do the trick (https://stackoverflow.com/questions/35450288/how-to-get-the-serial-number-of-usb-device-with-golang​), but only .../examples/get_sn/get_sn.go worked to a point(access denied) and the others yielded runtime errors. Since I haven't ever used go, it is probably easy to solve for others.

One other question: I have understood that jag installs a kind of wrapper around the toit-program that is uploaded to the esp32. Is there some plain language (other than source code) info, how that is done? And can you access the wrapper code from within the toit program?

kasperl commented 2 years ago

@lutzbickhardt The wrapper is written in Toit and it isn't much code. Here is where we get an http request to install a new program: https://github.com/toitlang/jaguar/blob/main/src/jaguar.toit#L84.

lutzbickhardt commented 2 years ago

fyi, solved the serial number thing

  1. neither github.com/gotmc/libusb nor github.com/google/libusb provided a direct port <-> s/n relation
  2. used the file system (Ubuntu; on a Mac the s/n is part of the port name, as in /dev/tty.usbserial-<s/n>) code (maybe not the most elegant, but -I hope- readable) ->

    package commands

import ( "fmt" "os/exec" "regexp" )

func GetSerialNumber(port string) (string, error) { //get serial number of USB connection (CP2104) //$ ls -l /sys/class/tty/ttyUSB0 //lrwxrwxrwx 1 root root 0 Jan 6 13:27 /sys/class/tty/ttyUSB0 -> ../../devices/pci0000:00/0000:00:08.1/0000:03:00.4/usb3/3-1/3-1:1.0/ttyUSB0/tty/ttyUSB0/ ttyStr := "/sys/class/tty" + port[4:] //skip /dev/ out, := exec.Command("ls", "-l", ttyStr).Output() match, := regexp.MatchString("usb", string(out)) if match { re := regexp.MustCompile(usb(?P<b>\d{1})/\d{1}-(?P<p>\d{1})) matches := re.FindStringSubmatch(string(out)) bIndx := re.SubexpIndex("b") pIndx := re.SubexpIndex("p")

    bpStr1 := "/sys/bus/usb/devices/usb" + matches[bIndx]
    bpStr2 := matches[bIndx] + "-" + matches[pIndx]
    bpStr := bpStr1 + "/" + bpStr2 + "/serial"

    out, _ = exec.Command("cat", bpStr).Output()
    //fmt.Printf("%s -> s/n: %s\n", port, string(out))
    rere := regexp.MustCompile(`\r?\n`)
    outStripped := rere.ReplaceAllString(string(out), " ")
    return outStripped, nil
} else {
    return "", fmt.Errorf("no corresponding USB port found with ls -l /sys/class/tty/ttyUSBn")
}

}

  1. expanding the config file and flashing and reading it in jaguar.toit went ok. ... INFO: [LB] running Jaguar M5core 'core2_WHITE' (id: 'bcd682c6-4d15-4a84-9018-71a1bfff0b72') on 'http://192.168.1.56:9000', s/n: 0225F061 ... So far, so good!

Question: how can I access/print the s/n from a flashed pgm (that is run under the vm). Do I have to communicate with jaguar.toit through http? or is there a vm import or global or built-in keyword? like: print vm.sn

kasperl commented 2 years ago

Cool stuff, @lutzbickhardt! I think the code you have for reading the config file (https://github.com/toitlang/jaguar/blob/main/src/jaguar.toit#L32) should also work from an application, so if the serial number is stored in the image_config, it might be easy to read it also from another app.

lutzbickhardt commented 2 years ago

voila! after adding import esp32 it works! image_config := {:} sn := "?s/n?" if platform == PLATFORM_FREERTOS: print "$PGM_ID accessing config ..."

image_config = esp32.image_config or {:}
print "$PGM_ID config found"

sn = image_config.get "sn" --if_absent=: sn
print "$PGM_ID s/n: $sn"

=> ... [WS_C_D] accessing config ... [WS_C_D] config found [WS_C_D] s/n: 0225F061 ...

kasperl commented 2 years ago

Starting with Jaguar v1.5.2, this is now possible using:

jag run -D my.value=42 examples/hello.toit

or for installed containers:

jag container install -D broker.host=test.mosquitto.org mqtt-service examples/mqtt.toit

The -D arguments show up as an arguments Map to main in the programs:

main arguments:
  print "arguments = $arguments
  value := arguments.get "my.value"
  print "my.value = $value" 

Thanks for filing this issue @lutzbickhardt!