Copyright © https://mongoose-os.com

Mongoose OS Forum

frame
ATTENTION! This forum has moved to:

https://community.mongoose-os.com

Do not post any new messages.

I2C.read function parameters

I am having a hard time understanding the parameters for the I2C read function for mjs, particularly what the data value is suppose to be.

I understand that the code is just the c conversion below however I don't know how to represent a char pointer in mjs.
read: ffi('bool mgos_i2c_read(void *, int, char *, int, bool)'),

I have the following code:
let handle = I2C.get_default();
let address = 0x50;
let stop = true;
let size = 7;
let stop = true;
let data = ""; //do I need to do a Object.create()?
I2C.read(handle, address, data, size, stop);

My understanding is that I need a char* for the third parameter, but I don't know how to create that. I have looked for examples and have found none for the read function. I feel like there is something really simple I am missing and do appreciate the help. Thank you as always :)

Comments

  • Thanks for the info. It looks like the guy in the first forum just did single quotes, so I am going to try that. I am relatively new to embedded programming so this helps. I am also trying to understand the difference between the readRegW , readRegB , & read as well but think I found my answer in the C api.

  • SergeySergey Dublin, Ireland

    readRegW reads word (2 byte register), readRegB reads byte (1 byte register).

    You can get an idea about RPC parameters this way:
    launch mos UI, click on the "Device services" tab, click on the service you want.

  • Michael_MooreMichael_Moore Los Angeles

    I've been struggling with for a while too, especially when interfacing with a BH1750FVI (high resolution light sensor) which requires an initialization write byte without the usual register selector. The RPC interface works fine, but I could not find any way to use the javascript API without sending a register selection.

    The best solution would seem to define javascript functions readB, readW, readN, writeB, writeW and writeN - corresponding to the existing register versions (readRegB etc.). This would require also creating the matching c functions in mgos_i2c.c

    In the end, I took a cruder approach, reserving a special value (255) for the reg parameter to mean "without reg" and changing mgos_i2c_read_reg_n, mgos_i2c_write_reg_b, mgos_i2c_write_reg_w, and mgos_i2c_write_reg_n to suppress setting the register when the magic 255 register value is specified. This turned out to be very simple, though I'm not happy with using magic reg values. I don't know of any i2c device using register 255, but there may be some.

    I have the code working if anyone is interested.

    Also, could never get the local build working under windows 10 - has anyone got this working recently? I ended up (after struggling with this for a day or two) installing Linux mint in a hypervisor which works perfectly.

  • SergeySergey Dublin, Ireland
    edited June 2017

    @Michael_Moore thanks for the background.

    https://github.com/cesanta/mongoose-os/blob/master/fw/mjs_api/api_i2c.js actually defines JS API for reading/writing registers, or am I missing something?

    I am very interested in your code.
    Please follow https://mongoose-os.com/docs/core_components/apps.html#contribute-app

  • Michael_MooreMichael_Moore Los Angeles

    Will do, but to clarify - the missing piece is for making non-register reads and writes. What do you feel about reserving a special register (say 255) to mean no-register, as opposed to creating new functions such as mgos_read_b, mgos_read_w, mgos_read_n, mgos_write_b, mgos_write_w and mgos_write_n (plus corresponding additional RPC and JS functions)? Magic values are ugly, but so is expanding the code footprint.

  • rojerrojer Dublin, Ireland
    edited June 2017

    "could not find any way to use the javascript API without sending a register selection."- this is what I2C.read and I2C.write functions do.
    to prepare a char * pointer for data, you can follow the example of readRegN - building up a string: https://github.com/cesanta/mongoose-os/blob/cf08cff8a1c2ac698ed75077c0728d564f629e52/fw/mjs_api/api_i2c.js#L34
    it's ugly, but works.

  • Michael_MooreMichael_Moore Los Angeles
    edited June 2017

    OK, this helps - but I'm not convinced the I2C.read javascript call actually works. I suspect there is a problem with the way character buffers are referenced.

    I'm trying to read from a BH1750 light sensor that requires a single byte 0x16 to be sent to begin continuous reading mode. Following your suggestion, I can do this as follows

    let i2c = I2C.get_default();
    let addr = 35;
    let buf1 = '\x16';
    I2C.write(i2c, addr, buf1, buf1.length, true);  // this works.
    

    The 16 bit result can now be read back. I confirmed this by calling the RPC I2CRead function, asking for 2 bytes. Perfect. I've traced the code and can see it calling the mgos_i2c_read function in mgos_i2c.c

    However, when I2C.read from javascript, the function appears to work (i.e. returns true), but the buffer passed does not get initialized. I'm pretty sure the function is getting a COPY of my javascript buffer, so the javascript buffer does not get filled in. See here, with debugging, level 3 turned on.

    Timer.set(10000 /* 10 sec */, true /* repeat */, function() {
      buf = 'xy';
      print("I2C.read()?", I2C.read(i2c, addr, buf, 2, true));
      print("buf.length?", buf.length);
      print("buf?", buf);
    }, null);
    

    The logs show this.

    mgos_i2c_read        read 2 from 35, start? 1, stop? 1
    mgos_i2c_start        addr 0x23, mode R => ab 0x47
    mgos_i2c_send_byte    sent 0x47, recd ACK
    mgos_i2c_read_byte    recd 0x2b, sent ACK
    mgos_i2c_read_byte    recd 0xa0, sent NAK
    mgos_i2c_stop         stop
    I2C.read()? true 
    buf.length? 2 
    buf? xy 
    

    As this shows, the 2 mgos_i2c_read_byte calls return a 16 bit light value (0x2ba0) and the call to I2C.read succeeds (returns true). The buffer passed to the I2C.read function does not get modified though.

  • Michael_MooreMichael_Moore Los Angeles
    edited June 2017

    UPDATE - I think I'm on to something. In C, string and char arrays can be accessed pretty much identically, but in javascript, strings are immutable. So when a javascript string is passed to the c function to be filled in, the string seen by javascript never changes.

    We could instead pass a javascript char array, like this

    let buf2 = ['\x33', '\x34'];  // any two characters, just to pre-populate the character array
    I2C.read(i2c, 35, buf2, 2, true);
    

    This, of course fails as the mgos_read_byte c function is expecting a char*

    MJS callback error: failed to call FFIed function: actual arg #2 is not a string (the type idx is: array)

  • @Michael_Moore , just a thought, can you use ffi to create a character pointer then save it as a variable?

  • SergeySergey Dublin, Ireland

    Reproduced this bug.

    The issue is with the string marshalling from JS to C. Long strings, > 5 chars, are marshalled correctly.
    Short strings, like xy, are not. Note, 5 char threshold is mJS optimisation: short strings are stored directly in the mjs_val_t 64-bit placeholder, whereas long strings are stored in the separate buffer.

    Fixing this, will update this thread when done.

  • SergeySergey Dublin, Ireland

    For now, you can use a kludge:

    let buf = '123456'; // minimum 6 byte string
    I2C.read(i2c, 35, buf, 2, true);   // Update first 2 bytes in buf
    
  • Michael_MooreMichael_Moore Los Angeles
    edited June 2017

    Excellent - what a clever trick :)

    Here is a trivial script to read light values from a BH1750 sensor

    load('api_i2c.js');
    load('api_timer.js');
    
    let i2c = I2C.get_default();
    let addr = 35;   // default i2c address for BH1750 (when addr pin low)
    
    let buf = '\x1701234';  // trick to avoid problem with marshalling string < 6 chars long back to javascript...
    I2C.write(i2c, addr, buf, 1, true);
    
    Timer.set(1000 /* 1 sec */, true /* repeat */, function() {
      if (I2C.read(i2c, addr, buf, 2, true)) {
        print("Light Reading?", Math.round(((buf.charCodeAt(0) * 256) + buf.charCodeAt(1))/1.2), "lux");
      }
    }, null);
    

    Thanks again.

    Mark this as SOLVED

  • SergeySergey Dublin, Ireland

    @Michael_Moore thanks a bunch !

    Do you mind if I wrap it as a library, and publish ?
    I'd like to list yourself as an author - let me know your FULL NAME <email>, or FULL NAME <github_id> please

  • Michael_MooreMichael_Moore Los Angeles

    No problem
    mdmoore@gmail.com
    michaeldmoore (on github)

    :)

Sign In or Register to comment.