what happens after "Serial".setConsole ?

Posted on
  • After calling Bluetooth.setConsole I get a timeout in IRQ.
    Before doing this, a BLE service with

    If consoleDevice = device; is commented, it works fine. Obviously on old settings.

    What happens after setting consoleDevice ?
    Do I have to add something to Bluetooth object ?

  • Well, it:

    • Initialises the device if it's not done already (I think jshUSARTSetup may get called for Bluetooth)
    • Outputs -> Bluetooth to the old device
    • Sets the console deviec
    • Outputs '<- OldDevice` to the new device
    • Prints the JS console characters (>)

    It's all here: https://github.com/espruino/Espruino/blo­b/master/src/jsinteractive.c#L168

    I can't remember anything else offhand, but you'd have to look at the source of jsiSetConsole.

    I guess it could be the Bluetooth serial write IRQ that's crashing? You need to have a way of taking data out of the txBuffer and transmitting it via Bluetooth from an IRQ - if you don't have that then the txBuffer will just get full and things will stall.

    Does just doing Bluetooth.write("sometext") cause problems, or does that work?

  • Hmmm,
    so I've to catch device EV_BLUETOOTH in jshUSARTSetup.
    Anyway, if EV_BLUETOOTH is already initialised this should be skipped.
    I see -> Bluetooth on console, up to that point everything is fine.
    If next step (set Console Device) is done, app runs into IRQ timeout.

    I cannot find the step, where something like Bluetooth.write is called. There should be a, let me say connection/function, to write data to ESP32 bluetooth device from Serial object.

    Walking through jswrap_serial and jsdevices, at the end, it should go to jshUSARTKick with device = EV_BLUETOOTH, is that correct ?

  • it should go to jshUSARTKick with device = EV_BLUETOOTH, is that correct ?

    Yes, and you can kick off Bluetooth transmission from there. However you still need a way to detect when a Bluetooth packet has finished sending so you can send the next one.

    Also, using USARTKick isn't ideal - it'll get called after the very first character. Ideally you want to be able to write multiple characters from the buffer at once.

    nRF52 uses a 'radio notification' event to do that: https://github.com/espruino/Espruino/blo­b/master/targets/nrf5x/bluetooth.c#L744

  • On my best understanding there is one major location to call USARTKick, which is jshTransmit
    jshTransmit is called with one char in jsdevices.c and in jswrap_serial.c
    In jsdevices its called from jshTransmitPrintfCallback which loops through a string
    In jswrap_serial its called from _jswrap_serial_print_cb which iterates through an arg (jsVar)
    Both cases a string is send with lot of calls to USARTKick. For Bluetooth it would be a better idea, to send strings, to avoid overhead.

    If I would now replace many calls to USARTKick by a function that sends a string, what would be the best way ?
    Adding an #ifdef USE_BLUEOOTH in jsdevices and jswrap_serial is doable, but would be ugly.
    We could add a call in bluetooth.h for strings
    Or add a call to jshardware.h like USART_SendString

    There are pros and cons for all solutions. Looks to me, like we have to change core sources in each case.
    What would be best, or is there another way to solve the problem.

  • None of those methods is great. It's virtually impossible to ever get a string of data that you can send. Sure, you can do a little better sometimes, but really not that much and there are loads of cases where it's very hard to improve.

    Can you not do what we do on nRF5x? Figure out what we need to send in an IRQ that happens just before data is sent?

    If not, how about:

    • In jshUSARTKick, schedule a Utility timer callback (jstimer.h) for 1ms if one hasn't already been scheduled (just have a bool for whether it has been scheduled or not).
    • When that fires, grab the data and transmit it
    • When the transmission finishes, if there is more data then transmit that too

    Or just skip the timer and send the first character on its own. That's what I did originally and it works reasonably well.

  • Hmm, tried to use timer. Something may be wrong in my understanding of jstExecuteFn
    Or, another option, I'm on the wrong train ?
    Short hint, addCharToNotif is called from jshUSARTKick

    [#define](http://forum.espruino.com/sear­ch/?q=%23define) notifBufferSize 10
    uint8_t notifBuffer[notifBufferSize];
    uint8_t notifBufferPnt = 0;
    bool inNotif = false;
    
    void sendNotifBuffer(){
        if(uart_gatts_if != ESP_GATT_IF_NONE){
            esp_ble_gatts_send_indicate(uart_gatts_i­f,0,uart_tx_handle,notifBufferPnt,notifB­uffer,false);
        }
        notifBufferPnt = 0;
    }
    void notifTimerCB(){
        if(notifBufferPnt > 0) sendNotifBuffer();
        jstStopExecuteFn(notifTimerCB,NULL);
        inNotif = false;
    }
    void startNotifTimer(){
        JsSysTime time = jshGetSystemTime();
        inNotif = true;
        jstExecuteFn(notifTimerCB, NULL, time , 10);//assumed that CB is called after 10 ms ??
    }
    void addCharToNotif(int c){
        notifBuffer[notifBufferPnt] = (uint8_t)c;
        notifBufferPnt++;
        if(notifBufferPnt >= notifBufferSize) sendNotifBuffer();
        if(!inNotif) startNotifTimer();
    }
    
  • I think you just want:

        JsSysTime period = jshGetTimeFromMilliseconds(10);
        jstExecuteFn(notifTimerCB, NULL, jshGetSystemTime()+period, 0);
    

    Setting a period will make the timer keep calling the function (like setInterval). Also the first call would have been scheduled right away.

    You could then avoid jstStopExecuteFn unless you're calling sendNotifBuffer direct from addCharToNotif when startNotifTimer has already been called.

    I'd also forget about notifBuffer - the characters should already have been pushed onto the FIFO so you could just pull them off of there when you're ready to send. For now it might be worth commenting out the notifBufferPnt >= notifBufferSize as it should work without it and it avoids complexity :)

    Realistically the polling interval for BLE connections will be something like 20ms, so having a 10ms delay really isn't going to slow anything down too much - in fact in 50% of cases it'll have no effect at all :)

    So I'd say something like:

    bool inNotif = false;
    
    void notifTimerCB(){
      char buf[BLE_NUS_MAX_DATA_LEN];
      int idx = 0;
      int ch = jshGetCharToTransmit(EV_BLUETOOTH);
      while (ch>=0) {
        buf[idx++] = ch;
        if (idx>=BLE_NUS_MAX_DATA_LEN) break;
        ch = jshGetCharToTransmit(EV_BLUETOOTH);
      }
      if (idx>0) {
        if(uart_gatts_if != ESP_GATT_IF_NONE){
            esp_ble_gatts_send_indicate(uart_gatts_i­f,0,uart_tx_handle,idx,buf,false);
        }
      } else { // no data to transmit
        jstStopExecuteFn(notifTimerCB,NULL);
        inNotif = false;
      }
    }
    
    void startNotifTimer(){
        inNotif = true;
        JsSysTime period = jshGetTimeFromMilliseconds(10);
        JsSysTime time = jshGetSystemTime();
        jstExecuteFn(notifTimerCB, NULL, time+period, period);
    }
    
    void jshUSARTKick(IOEventFlags device) {
      if (device == EV_BLUETOOTH) {
        if(!inNotif) startNotifTimer();
      }
    }
    
  • Free translation of a german proverb is,
    You can grow as old as a cow
    and still increase knowhow
    :-)

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

what happens after "Serial".setConsole ?

Posted by Avatar for JumJum @JumJum

Actions