Programming Arduino Sketch <--> Android App

Discussion in 'UDOO NEO' started by frudman, Oct 19, 2016.

  1. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    We've been using the Quad (running Android and interfacing with a complex Arduino sketch) and are now trying to switch over to the Neo.

    Following the instructions on Neo's docs it says that to program Arduino using external PC (or Mac, in our case) we need to load UDOOUbuntu on the Neo. Is that right?

    If so, does this mean that we can't test Arduino with Android together and must first do 1 (e.g. Arduino with Ubuntu) then the other (e.g. Android)? Namely switch the microSD-card OS to test proper comm between our Android app and the Arduino sketch???

    On the Quad we simply had to pull/push the pin on the board to re-route the usb channel (temporary enabling/disabling Android adk comm flow).

    But it sounds like on the Neo, we need to switch the whole micro-sd card. And write a linux-based app to test comm before going back to Android, then retest it there.

    Am I missing something? Any help/direction is welcome.



    Freddy.
     
  2. waltervl

    waltervl UDOOer

    Joined:
    Dec 12, 2015
    Messages:
    2,314
    Likes Received:
    580
    That is a good question! I never played with Android on the Neo as it has not been mature yet. But looking at the documentation there is no way to program the M4 at the moment.
    Your supposed SD card switch method to switch between Udoobuntu and Android will not work as you have to reboot. During reboot the M4 will be wiped, so no program.

    In Udoobuntu there is a script that loads the last compiled Arduino sketch into memory of the M4. Perhaps there is a possibility to add the same script to the startup of Android?

    There could also be a possibility to load udoofota into Android so you can program the M4 from an external PC. https://github.com/UDOOboard/udoofota
     
  3. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    if that's the case, that means using Android/Neo with Arduino is not possible? Wow, that would be a real bummer (and deal killer for our app).
    What is udoofota? How would I load this onto android?
     
  4. waltervl

    waltervl UDOOer

    Joined:
    Dec 12, 2015
    Messages:
    2,314
    Likes Received:
    580
    I updated my reply with the link to udoofota.
    I suppose the Udoo team eventually will add Arduino programming to their Neo Android (AndrUdoo? :))
    You will have to contact @Andrea Rovai for their plans with it.
     
  5. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    That's very disappointing. We may have to go back to the Quad for now.
    I'll see if I can contact Andrea and what he knows.
    Thanks.
     
  6. waltervl

    waltervl UDOOer

    Joined:
    Dec 12, 2015
    Messages:
    2,314
    Likes Received:
    580
    I see that in https://github.com/UDOOboard/udooneo-m4uploader there is an Android.mk file. I don't know anything from Android and you have to check if it is in your image. But with this tool you can load the Arduino part with a precompiled sketch.
     
  7. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    I see the Android.mk file but it's not clear to me how to use this code. Load it as part of kernel/source-recompile of Android? As a separate .so lib? I think this is an android's work-in-progress for udoo neo's.
     
  8. Francesco

    Francesco Active Member

    Joined:
    Jun 23, 2015
    Messages:
    220
    Likes Received:
    110
    Hi. Yes, this is a work in progress. udooneo-m4uploader is being ported to Android (it already partially works), udoofota will be too.
    This week I am abroad, the next one I will work on that. I can share a beta with you if you want to test Android+Arduino.
     
  9. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    That's great news. And Yes, I would love to try out any beta you may have (in the form of an Android image? toolset?) and report on results.
    In the meantime we went ahead and created some Andoid code that was able to access (in a very hacked fashion) the Arduino side and we were able to "fake" Arduino's HelloWorld setup (i.e. the button/led test). That was very encouraging.
    I'm happy to share this code with you if you'd like (though our environment is different than most: we use C# with Xamarin to do Android development, a much simpler and better performing method).
     
  10. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    As promised, here's the code for our tests. It's partial and built for C# but it works well and should give anyone interested a template to convert to Java (no comments on quality, please: this was written as throw-away code).

    The test activity:
    Code:
        [Activity (MainLauncher = false)]
        public class TestingArduinoHarness : Activity
        {
            private const float textSize = 20f;
    
            protected override void OnCreate (Bundle savedInstanceState)
            {
                base.OnCreate (savedInstanceState); // MUST be called
    
                var screen = new LinearLayout (this) { Orientation = Orientation.Vertical }.setBackgroundColor (agr.Color.Aqua);
                SetContentView (screen);
    
                var neo = new SimplytelUtils.Adroid.Neo_Arduino_Tests_From_Android (this);
    
                new Button (this) { Text = "test#1", TextSize = textSize }
                    .setBackgroundColor (agr.Color.Blue)
                    .setLinearLayout (-1, -2) // hack: -2 == wrap, -1 == match/fill
                    .setPadding (30)
                    .setMargins (bottom: 10)
                    .addToView (screen)
                    .addOnClick ((src, ea) => {
                        ((Button)src).setBackgroundColor (neo.test1 () ? agr.Color.Green : agr.Color.Blue);
                    });
            }
        }
    
    (part II follows: forum message size limit)
     
    Andrea Rovai likes this.
  11. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    And the test part:
    Code:
        public class Neo_Arduino_Tests_From_Android
        {
            //private Activity parentActivity;
            public Neo_Arduino_Tests_From_Android (Activity parentActivity)
            {
                //this.parentActivity = parentActivity;
            }
    
            public class NeoBoard : UDOOBoard
            {
                public NeoBoard (bool displayCmdDetails = false) : base(displayCmdDetails)
                {
                    // external pins from: http://www.udoo.org/docs-neo/img/gionji/DOCS_external_pinout.PNG
                    // internal pins from: http://www.udoo.org/docs-neo/img/gionji/DOCS_internal_pinout.PNG
    
                    const int na = -1; // not applicable
    
                 // addPin(connector, internalPCBName, internalGPIO, externalPCBName, externalGPIO)
    
                    addPin ("J7", na, na, 47, 4);
                    addPin ("J7", na, na, 46, 5);
                    addPin ("J7", na, na, 45, 6);
                    addPin ("J7", na, na, 44, 7);
                    addPin ("J7", na, na, 43, 116);
                    addPin ("J7", na, na, 42, 127);
                    addPin ("J7", na, na, 41, 124);
                    addPin ("J7", na, na, 40, 119);
    
                    addPin ("J5", na, na, 39, 174);
                    addPin ("J5", na, na, 38, 175);
                    addPin ("J5", na, na, 37, 176);
                    addPin ("J5", na, na, 36, 177);
                    addPin ("J5", na, na, 35, 202);
                    addPin ("J5", na, na, 34, 203);
    
                    addPin ("J6", na, na, 33, 21);
                    addPin ("J6", na, na, 32, 20);
                    addPin ("J6", na, na, 31, 19);
                    addPin ("J6", na, na, 30, 18);
                    addPin ("J6", 13, 102, 29, 17);
                    addPin ("J6", 12, 100, 28, 16);
                    addPin ("J6", 11, 147, 27, 15);
                    addPin ("J6", 10, 146, 26, 14);
                    addPin ("J6", 9, 148,  25, 22);
                    addPin ("J6", 8, 105,  24, 25);
    
                    addPin ("J4", 7, 149, 23, 124);
                    addPin ("J4", 6, 140, 22, 182);
                    addPin ("J4", 5, 141, 21, 173);
                    addPin ("J4", 4, 142, 20, 172);
                    addPin ("J4", 3, 143, 19, 181);
                    addPin ("J4", 2, 104, 18, 180);
                    addPin ("J4", "Tx", 179, 17, 107);
                    addPin ("J4", "Rx", 178, 16, 106);
                }
            }
    
            public class UDOOPin
            {
                public enum PinMode { INPUT, OUTPUT };
                public enum PinValue { LOW, HIGH };
    
                public enum ConnectorRow { INTERNAL, EXTERNAL };
    
                public string pcbName;
                public int gpio = -1; // e.g. the Analog pins (A0-A5)
                public ConnectorRow row;
                public string connector; // e.g. J4, J5, J6, J7
    
                // file system names (how pins are accessed and controlled)
                private string gpioFS => $"/sys/class/gpio/gpio{gpio}";
                private string directionFS => $"{gpioFS}/direction";
                private string valueFS => $"{gpioFS}/value";
    
                private readonly SudoExec sh;
    
                public UDOOPin (string connector, string pcbName, int gpio, bool isInternal, SudoExec sh)
                {
                    this.connector = connector;
                    this.pcbName = pcbName;
                    this.gpio = gpio;
                    this.row = isInternal ? ConnectorRow.INTERNAL : ConnectorRow.EXTERNAL;
                    this.sh = sh;
                }
    
                public async Task<bool> isExported ()
                {
                    return (await sh.execWithResult ($"ls /sys/class/gpio", 200)).ciContains ($"gpio{gpio}");
                }
    
                public UDOOPin export ()
                {
                    sh.exec ($"echo {gpio} > /sys/class/gpio/export", 500); // give it a little longer to process
                    return this;
                }
    
                public async Task<PinValue> pinValue ()
                {
                    return (await sh.execWithResult ($"cat {valueFS}")).Trim ('\r', '\n').ciEquals ("0") ? PinValue.LOW : PinValue.HIGH;
                }
    
                public UDOOPin pinValue (PinValue value)
                {
                    var actual = (value == PinValue.HIGH) ? 1 : 0;
                    sh.exec ($"echo {actual} > {valueFS}");
                    return this;
                }
    
                public async Task<PinMode> pinDirection ()
                {
                    return (await sh.execWithResult ($"cat {directionFS}")).Trim ('\r', '\n').ciEquals ("out") ? PinMode.OUTPUT : PinMode.INPUT;
                }
    
                public UDOOPin pinMode (PinMode value)
                {
                    var actual = (value == PinMode.OUTPUT) ? "out" : "in";
                    sh.exec ($"echo {actual} > {directionFS}");
                    return this;
                }
    
                public async Task dumpInfo ()
                {
                    try {
                        Console.Write ($"{connector}-{row}-{pcbName}(gpio={gpio}): ");
                        if (await isExported ())
                            Console.WriteLine ($"{await pinDirection ()}-{await pinValue ()}");
                        else
                            Console.WriteLine ("NOT EXPORTED");
                    } catch (Exception ex) {
                        Console.WriteLine ($"{ex.Message}");
                    }
                }
            }
    
            public abstract class UDOOBoard
            {
                private readonly SudoExec sh; // process used to read/write to the Arduino side
    
                // dict is by PCB-NAMES
                private readonly ciDictionary<UDOOPin> allPins = new ciDictionary<UDOOPin> ();
    
                protected UDOOBoard (bool displayCmdDetails = false)
                {
                    sh = new SudoExec (displayCmdDetails);
                }
    
                public async Task dumpAllPins ()
                {
                    var pins = allPins.Values.OrderBy (pin => pin.connector + pin.pcbName);
                    foreach (var pin in pins)
                        await pin.dumpInfo ();
                }
    
                public UDOOPin pinByPcbName (string pcbName)
                {
                    UDOOPin pin;
                    return allPins.TryGetValue (pcbName, out pin) ? pin : null;
                }
    
                public UDOOPin pinByPcbName (int pcbPin)
                {
                    UDOOPin pin;
                    return allPins.TryGetValue (pcbPin.ToString (), out pin) ? pin : null;
                }
    
                public UDOOPin pinByGPIOName (int gpio)
                {
                    return allPins.Values.FirstOrDefault (pin => pin.gpio == gpio);
                }
    
                protected void addPin (string connector, int internalPCBPin, int internalGPIO, int externalPCBPin, int externalGPIO)
                {
                    if (internalPCBPin >= 0)
                        allPins [internalPCBPin.ToString ()] = new UDOOPin (connector, internalPCBPin.ToString(), internalGPIO, true, sh);
               
                    if (externalPCBPin >= 0)
                        allPins [externalPCBPin.ToString ()] = new UDOOPin (connector, externalPCBPin.ToString (), externalGPIO, false, sh);
                }
    
                protected void addPin (string connector, string internalPCBName, int internalGPIO, int externalPCBPin, int externalGPIO)
                {
                    allPins [internalPCBName] = new UDOOPin (connector, internalPCBName, internalGPIO, true, sh);
    
                    if (externalPCBPin >= 0)
                        allPins [externalPCBPin.ToString ()] = new UDOOPin (connector, externalPCBPin.ToString (), externalGPIO, false, sh);
                }
    
                public bool closed => sh.closed;
                public void close () => sh.close ();
            }
    
    
    (part III to follow)
     
  12. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    part III...

    Code:
            public class SudoExec
            {
                // opens a long running process that launches 'su' (sudo shell for udoo boards)
                // - process runs on its own thread/task
                // - commands are written to process' standardinput
                // - command results/output then read from std/err streams
    
                // max time for a command to generate each line of output; average (from tests) is 1-2ms; 25ms should be mostly safe
                public const int CMD_TIMEOUT_MS = 25; // a command is considered complete when CMD_TIMEOUT_MS has elapsed after last line written to std/err
    
                public bool closed { get; private set; } = false;
    
                public void close () => cmds.CompleteAdding (); // enables user to terminate this process/task
    
                // commands are executed in order received
                private readonly BlockingCollection<Command> cmds = new BlockingCollection<Command> (new ConcurrentQueue<Command> ());
    
                public class Command
                {
                    public string cmdLine;
                    public int timeout = CMD_TIMEOUT_MS; // ms after cmd executed or last write to std/err (whichever is later)
                    public Action<string> onResults = null;
    
                    public int id = cnt++;
    
                    private static int cnt = 0;
                }
    
                public SudoExec (bool showDebug = false)
                {
                    // simple debugging details
                    Action<string> log = str => { if (showDebug) Console.WriteLine (str); };
                    Action<string> logAsPrompt = str => { if (showDebug) Console.Write (str); };
    
                    log ("SU Initing");
    
                    var sudoShell = new System.Diagnostics.Process ();
                    var firstTimeout = 8 * 1000;  // give user a chance (8 seconds) to grant su admin priviledges (SU dialog pops up)
    
                    Command cmd = null; // next command to be executed
    
                    // gatekeeper forces each command to complete before the next one is started
                    var gatekeeper = new AutoResetEvent (true); // gate starts open [true=SET=signaled=ready-to-go; false=RESET=non-signaled=wait-to-go]
    
                    // wathchdog timer ensures that a given command's execution is [considered] completed once timer expires
                    var watchDog = new System.Timers.Timer { AutoReset = false }; // autoreset false because we explicitly start/stop this timer
    
                    var received = new StringBuilder (); // what's received for executing command (i.e. its std/err output)
                    var lastReceived = DateTime.Now; // keep track of when last output was received from executing command (as fyi)
    
                    // as per https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput(v=vs.110).aspx
                    sudoShell.StartInfo.UseShellExecute = false;
                    sudoShell.StartInfo.RedirectStandardInput = true; // where we'll WRITE the commands
                    sudoShell.StartInfo.RedirectStandardOutput = true; // where we'll READ the response of the commands
                    sudoShell.StartInfo.RedirectStandardError = true;
    
                    // Create a single running (non-stopping) shell
                    sudoShell.StartInfo.FileName = "su";
    
                    Action<string, string> logElapsed = (what, data) => {
                        var elapsed = DateTime.Now - lastReceived;
                        lastReceived = DateTime.Now;
                        log ($"{data}...{elapsed.TotalMilliseconds}ms from last {what}");
                    };
    
                    sudoShell.OutputDataReceived += (sender, e) => {
                        // called each time a LINE is received
                        // ...does not SEEM to include newline chars, which is different than msdn docs
                        // ...as per: https://msdn.microsoft.com/en-us/library/system.diagnostics.process.outputdatareceived(v=vs.110).aspx
                        if (closed)
                            log ($"...closing shell: ignoring final output: [{e.Data}]");
                        else {
                            received.AppendLine (e.Data); // note: newline character added here by us (because not present from e.data: tbv)
                            watchDog.Interval = cmd.timeout; // resets it
                            logElapsed ("std", e.Data);
                        }
                    };
    
                    sudoShell.ErrorDataReceived += (sender, e) => {
                        // called each time a LINE is received
                        // ...does not SEEM to include newline chars, which is different than msdn docs
                        if (closed)
                            log ($"...closing shell: ignoring final error output: [{e.Data}]");
                        else {
                            received.AppendLine (e.Data); // note: newline character added here by us
                            watchDog.Interval = cmd.timeout; // resets it
                            logElapsed ("err", e.Data);
                        }
                    };
    
                    watchDog.Elapsed += (sender, e) => {
    
                        var elapsed = DateTime.Now - lastReceived;
                        log ($"...command #{cmd.id} expired [after {elapsed.TotalMilliseconds}ms]");
    
                        if (cmd.onResults != null)
                            cmd.onResults (received.ToString ());
    
                        gatekeeper.Set (); // open the gate by signaling it (sets it to true=available)
                    };
    
                    sudoShell.Start ();
                    log ("SUDO SHELL PROCESS Started...");
    
                    sudoShell.BeginOutputReadLine ();
                    sudoShell.BeginErrorReadLine ();
                    log ("...SU SHELL now listening");
    
                    Task.Run (() => {
    
                        log ("SUDO Shell TASK Loop STARTED");
    
                        while (!cmds.IsCompleted) {
    
                            gatekeeper.WaitOne (); // because it's AUTO-reset (i.e. call to .Reset() is implicit), blocks next go around (following end of this block)
    
                            try {
                                logAsPrompt ("\r\ncommand #");
                                cmd = cmds.Take (); // will block until one is ready
                                log ($"{cmd.id}> {cmd.cmdLine}");
                            } catch (InvalidOperationException) {
                                log ("exit shell"); // write a "pseudo command" (will be displayed at "pseudo prompt")
                                break;
                            } catch (Exception ex) {
                                log ($" error listening for command: {ex.Message}");
                                break;
                            }
    
                            received.Clear ();
                            lastReceived = DateTime.Now;
                            sudoShell.StandardInput.WriteLine (cmd.cmdLine);
                            watchDog.Interval = Math.Max (firstTimeout, cmd.timeout);
                            firstTimeout = 0; // only needed one-time: once SuperSU grants an app access, it's permanent (until app is reloaded through debug)
                            watchDog.Start ();
                        }
    
                        log ("SUDO Shell TASK Loop Terminated as per user");
    
                        closed = true;
                        sudoShell.StandardInput.Close ();
                        sudoShell.WaitForExit ();
                        sudoShell.Close ();
                        sudoShell = null;
                        log ("SUDO Shell PROCESS CLOSED by user");
                    });
                }
    
                public void exec (string cmd, int timeout = CMD_TIMEOUT_MS)
                {
                    if (closed)
                        throw new Exception ($"shell already closed: command '{cmd}' ignored");
    
                    cmds.Add (new Command { cmdLine = cmd, timeout = timeout });
                }
    
                public Task<string> execWithResult (string cmd, int timeout = CMD_TIMEOUT_MS)
                {
                    if (closed)
                        throw new Exception ($"shell already closed: command '{cmd}' ignored");
    
                    var tsk = new TaskCompletionSource<string> (); // this is like a Promise (where we can .setResult() when we're ready)
    
                    cmds.Add (new Command { cmdLine = cmd, onResults = res => tsk.SetResult (res), timeout = timeout });
    
                    return tsk.Task;
                }
            }
    
            private UDOOBoard udoo = null;
            public bool test1 ()
            {
    
                if (udoo == null || udoo.closed) {
                    //Task.Run (async () => {
                    Task.Run (() => {
                        udoo = new NeoBoard (false);
                        //await udoo.dumpAllPins ();
                        neoButtonTest (udoo);
                    });
                    return true; // running
                } else {
                    udoo.close ();
                    return false; // not running
                }
            }
    
            private void neoButtonTest (UDOOBoard board)
            {
                // based on: https://www.arduino.cc/en/Tutorial/Button
                // also read: http://www.ladyada.net/learn/arduino/lesson5.html
    
                const int buttonPin = 2;
                const int ledPin = 13;
    
                // SETUP
                var btn = board.pinByPcbName (buttonPin).export ().pinMode(UDOOPin.PinMode.INPUT);
                var led = board.pinByPcbName (ledPin).export ().pinMode (UDOOPin.PinMode.OUTPUT);
    
                // LOOP (on its own thread)
                Task.Run (async () => {
                    while (!board.closed) {
                        led.pinValue (await btn.pinValue ());
                        await Task.Delay (100); // checks 10 times per second: good enough for proof-of-concept
                    }
                });
                   
            }
        }
     
    waltervl likes this.
  13. Francesco

    Francesco Active Member

    Joined:
    Jun 23, 2015
    Messages:
    220
    Likes Received:
    110
    Last edited: Nov 4, 2016
    waltervl likes this.
  14. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    That's great! I'll download it and try it right away. Thanks a lot.
    Freddy.
     
  15. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    OK, I've downloaded the image and will now create some USD cards to test.
    A few quick questions:

    1. I use the serial cable to program the android side (A9). Will that stop working when programming the M4 side? Or can we do this concurrently (a common setup for us as we change code on both at once as we try/test things)
    2. If we go over-the-air (since it's not clear how to do this otherwise right now), I'll change the IP to our local test network [192.168.1.x] in the Arduino15 settings file: but does this mean that the usb-coonect cable (for the android side, via adb) will stop working (since it's not longer the default of [192.168.7.2:5152]? Or will ADB auto-detect it?
    3. On the Quad we used the Accessory ADK API to connect the Arduino-side to the Android side (programmatically). Is that still the same or is there a different/better way (e.g. memory mapping? read ttyMMC? something else?)
    4. If through reading /dev/ttyMMC (which would be great and simple!), is this a blocking read on the android side (which would be the best :))? If so, that would be great because we could just launch a separate task/thread to simply read that stream (which would block when there's nothing) then get interrupted only when there's something that needs the app's attention. Otherwise, we'd need to constantly poll (even if on a separate thread) which would not be a great setup. Let me know. Thanks.

    Let me know. In the meantime, I'll see how far I can get on my own.

    Thanks for all the work. Well appreciated.
    Freddy.
     
    Last edited: Nov 3, 2016
  16. Miguel Palacios

    Miguel Palacios New Member

    Joined:
    Nov 1, 2016
    Messages:
    9
    Likes Received:
    0
    [Quote = "frudman, mensaje: 22076, miembro de: 35731"] OK, he descargado la imagen y creará ahora algunas tarjetas de USD para probar.
    Unas pocas preguntas rápidas:

    1. Yo uso el cable serie para programar el lado androide (A9). Habrá que dejar de trabajar en la programación del lado M4? ¿O podemos hacer esto al mismo tiempo (una configuración común para nosotros a medida que cambiamos código en ambos a la vez ya que tratamos / cosas de prueba)
    2. Si vamos over-the-air (ya que no está claro cómo hacer esto de otra manera en este momento), voy a cambiar la IP de nuestra red local de ensayo [192.168.1.x] en el archivo de configuración Arduino15: pero ¿Significa esto que el cable USB-coonect (por el lado androide, a través de ADB) dejará de funcionar (ya que no es más largo sea el predeterminado [192.168.7.2:5152]? ¿O ADB auto-detecta?
    3. En el Quad hemos utilizado la API de accesorios ADK para conectar el lado de Arduino para el lado de Android (programación). Es que sigue siendo el mismo o hay una mejor manera diferente / (por ejemplo, la asignación de memoria? TtyMMC leer? Otra cosa?)
    4. Si través de la lectura / dev / ttyMMC ( lo que sería grande y simple! ), Se trata de un bloqueo de lectura en el lado androide (que sería el mejor :))? Si es así, eso sería muy bueno porque podríamos lanzar una tarea / subproceso independiente para simplemente leer esa corriente (que bloquearía cuando no hay nada) entonces se les interrumpe sólo cuando hay algo que necesita atención de la aplicación. De lo contrario, tendríamos que sondear constantemente (incluso si en un hilo separado) que no sería una gran disposición. Házmelo saber. Gracias.

    Házmelo saber. Mientras tanto, voy a ver hasta dónde puedo conseguir en mi propia.

    Gracias por todo el trabajo. Bien apreciado.
    Freddy. [/ Quote]

    Hola, can you up a code of arduino in your udoo neo with android?
     
  17. waltervl

    waltervl UDOOer

    Joined:
    Dec 12, 2015
    Messages:
    2,314
    Likes Received:
    580
  18. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    Working on it as we speak! No results yet (needed to redo work env with Arduino). Will let you know as soon as I know.
     
  19. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    Very Good News: It WORKS!!! :)

    Did very preliminary test:
    1. just loaded beta-2 image on uSD card (plain, not even google play)
    2. followed the instructions on this link to install Arduino IDE on a Mac
    3. Restart IDE (must restart to reload new IP address)
    4. Literally copied this code into the Arduino IDE (our hello world!)
    And Voila!!! Let there be light! :)

    All Good so far. Will be doing more test today and tomorrow for communication between android/a9 and arduino/m4

    UPDATE: I was also able to simultaneously load an Android app while arduino ide connected via network. This will make it much much faster to develop an app (relative to method for the quad) because there's no need to select 1 path or the other (arduino or otg-connector) for inter-chip comm. Yey!!!

    Freddy

    ps: just for good measure: :)
     
    Last edited: Nov 3, 2016
    waltervl likes this.
  20. frudman

    frudman Member

    Joined:
    Apr 15, 2014
    Messages:
    64
    Likes Received:
    14
    OK, first stumbling block: I am not able to "read" /dev/ttyMCC" from android (I get "Access to the path "/dev/ttyMCC" is denied").

    Not sure how to get past this. In Android you can read/write files and I was hoping this would be enough to communicate through Serial (on Arduino side). There's also something called Android Channels but it's not clear how to use this for 2-way comm.

    Or maybe using USB-as-Serial but not clear how yet, though I did find this little bit. I'll explore this a little bit and see if it goes anywhere.

    Will revisit in the morning...
     

Share This Page