Monthly Archives: August 2014

AC/DC

A couple of days ago I purchased a DC adapter with a variable DC voltage output, and today I finally got around to connecting it to my Brick. It was scary but worked out beautifully. Look!bild 1

And this is what it will look like on the backside.bild 2

 

It’s just a proof that it is possible, so next step is to make it prettier.  :)  <3

Measurements of electrical current

So I’ve done some measurements on amperes used in various situations on the EV3.

My main takeaway is that once you’ve done something the Brick will need more power, even though you’ve shut down or disconnected the thing that needed more power.

The unit used for my figures is mA and the multimeter I used could only go up to 200 mA so that’s why a lot of the measurements only say >200. That means the Brick was using more than 200 mA. So here are my measurements:

Booting the Brick: Varying between 100 to >200
Idle: 120
Running my Arholma app: 100-110
Running my transparency app: 100-115

Bluetooth active but not paired: 120
Bluetooth pairing/paired: 130
Bluetooth paired and running commander: 115-160
Bluetooth disconnected: 135
Bluetooth disabled: 135

Pairing computer
Searching: 145
Pairing question: 145
Paired with computer: 134
Connected in labview: 138
Memory browsing: 138-140
Downloading file: 140

Shutting down: 100

Idle after reboot: 120
Wifi inserted: 145
Wifi starting: Peak >200, land at 151
Wifi connections: Peak >200, land at 154
Connecting to our wifi: Peak >200, land at 194
Connect to Brick via telnet: >200
Logged in: 197-199
Start SSH service on the Brick: >200
After shutting off wifi: 149
After unplugging the wifi dongle: 127

Idle after reboot: 125
Connected big motor: 139
Motor control app: 142
Running big motor: >200
After running big motor once: 167
After shutting down motor control app: 138
Disconnected big motor: 129

Connected touch sensor: spike 135, land at 130
Touching touch sensor without any app listening: 131
Started port view: 132
Touching in port view: 130
Exiting port view: 129
Disconnected touch sensor: 129

Connected light sensor: spike 140, land 138
Starting port view: 139-140
Exiting port view: 138
Disconnected light sensor: 129

Connected ir: spike 139, land 137
Started port view: 138
Using IR sender: 140
Exit port view: 138
Disconnect ir sensor: 129

Idle: 135
Connected small motor: spike 145, land 143
Started motor control: 140
Running small motor: >200
After running small motor: 167
Exit motor control: 140
Disconnect small motor: 135

Drained batteries and test art

Hello my lovely Lego friends!

There haven’t been a lot of updates in a while. Mostly life things been happening. There have been two threads running in my heads though.

  1. Creating an AC adapter for my EV3
  2. Ideas for the game I want to make

Thread 1: An AC adapter

So I noticed when plugging my wifi dongle into the EV3 that the batteries got drained quicker than .. something that gets drained really fast. Like. A drain pipe that have just been cleaned. Anyways, so I’ve been thinking about creating my own AC adapter. I figure it shouldn’t be more difficult than finding an AC adapter that matches the voltage and electric current of 6 AA-batteries.

The voltage is easy enough, it’s just 1.5V * 6 batteries, so 9V.

The current is tricker. I understand that the current of my AC adapter will decide the limit that the EV3 can get, and it will not force more current onto the Brick, so I could just get an adapter that provide a whole lot of current. But I want to be at least in the ballpark of what the EV3 needs. So I’ve gotten hold of a multimeter. (I’ve had to look up the english translation for these electrical terms, and I’ve got to say, I’m a bit disappointed that a multimeter is called a multimeter in English too)

So I’m planning on plugging the multimeter between the last battery and the device and see how much current it consume under different circumstances like idle, with motors running and with sensors running. So that’ll be fun! I’ll let you peeps know my findings.

Thread 2: Ideas for the game

I don’t really want to give too much away yet. Not that I’m aiming to make an announcement or anything, but I’d like to have the idea a little bit more concrete before I say anything. But I will show you another picture. This time I’ve been working on a test scene. Some parts of the image I’m happy with and some parts not so much. In any case, here it is:

Test scene

Splash screen and image converter

All this experimenting needs to lead somewhere. I am doing it partly to learn a new platform and partly because I want to make a game for it. I am not sure how to incorporate all the motors and sensor into the game, but I want them to be there somehow. Why bother making the game for the EV3 if I don’t use any of the hardware.

Anyways, I’ve started with the most essential part of the game. The splash screen. :)Game splash pt. 1

To get it onto the Brick I also created an image converter, that can read any file that SDL supports and create an RGF file.

The RGF file format is dead simple. First there are two unsigned byte, one for width and one for height of the image. Then each pixel in the image is stored in a bit. So 8 pixels are stored in 1 byte. Left most pixel is stored in the least significant bit, and rightmost pixel is stored in most significant bit.

This is the source I ended up writing. As you can see I have a hardcoded string for the in and out file names instead of using argv[1] and argv[2], but .. you know. Gotta keep stuff that can be improved. :)

//
//  main.cpp
//  bmpconverter
//
//  Created by Magnus Runesson on 2014-08-17.
//  Copyright (c) 2014 Magnus Runesson. All rights reserved.
//

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include "CImage.h"

int main(int argc, const char * argv[])
{
    CImage* pImage = new CImage( "splashfont2.png" );

    int outwidth = pImage->m_Width>>3;
    if( pImage->m_Width&7 )
        outwidth++;
    int outsize = outwidth * pImage->m_Height;
    
    unsigned char* outpixels = new unsigned char[ outsize ];

    int x, y;
    int readx;
    int writex;
    int inofs = 0;
    for( y=0; y<pImage->m_Height; y++ )
    {
        readx = pImage->m_Width;
        for( writex=0; writex<outwidth; writex++ )
        {
            int readpixelcount = 8;
            if( writex == outwidth-1 )
                readpixelcount = readx;
            readx -= readpixelcount;
            
            int outofs = (y*outwidth) + writex;

            unsigned char outbyte = 0;
            
            int i;
            for( i=0; i<readpixelcount; i++ )
            {
                unsigned char r = pImage->m_aPixels[ inofs + 0 ];
                unsigned char g = pImage->m_aPixels[ inofs + 1 ];
                unsigned char b = pImage->m_aPixels[ inofs + 2 ];
                unsigned char a = pImage->m_aPixels[ inofs + 3 ];
                
                inofs += 4;
                
                if( a > 127 )
                {
                    if( r < 127 )
                        outbyte |= (1<<i);
                }
            }
            
            outpixels[ outofs ] = outbyte;
        }
    }

    FILE* fout = fopen("splashfont2.rgf", "wb");
    unsigned char widthub = pImage->m_Width;
    unsigned char heightub = pImage->m_Height;
    fwrite( &widthub, 1, 1, fout );
    fwrite( &heightub, 1, 1, fout );
    fwrite( outpixels, 1, outsize, fout);
    fclose(fout);
    
    return 0;
}

So as long as you can read a file into a 32 BPP array in the order I read them you can pretty much plug any image reader into this.

I use SDL for reading images. Here is my CImage class

/*
 *  CImage.h
 *
 *  Created by Magnus Runesson on 1/28/12.
 *  Copyright 2012 Pokewhat. All rights reserved.
 *
 */

#pragma once

class SDL_Surface;

class CImage
{
public:
    CImage(const char* _pszFileName);
    CImage(CImage* _pReference, CImage* _pDiff);
    CImage(unsigned char* _aPixels, int _w, int _h);
    ~CImage();
    void SaveRAW(const char* _pszFileName);

private:
    unsigned int GetRGBA(SDL_Surface* _pSurface, int _x, int _y);
    void LoadSDL(const char* _pszFileName);

public:
    unsigned char* m_aPixels;
    char* m_pszFileName;
    int m_Width, m_Height;
};
/*
 *  CImage.cpp
 *
 *  Created by Magnus Runesson on 1/28/12.
 *  Copyright 2012 Pokewhat. All rights reserved.
 *
 */

#include "CImage.h"
#include "SDL_Image/SDL_Image.h"

void* fileRead(const char* _pszFileName, int* _pDatasize);
void fileWrite(const char* _pszFileName, void* _pImageData, int _Length);
void _fileFree(void* _pData, const void* _pReadData, size_t _Size);

void* fileRead(const char* _pszFileName, int* _pDatasize)
{
    FILE* f = fopen(_pszFileName, "rb");
    if( !f )
        return NULL;
    
    fseek(f, 0, SEEK_END);
    int size = ftell(f);
    fseek(f, 0, SEEK_SET);
    
    void* pRet = malloc(size);
    
    fread(pRet, 1, size, f);
    fclose(f);
    
    *_pDatasize = size;
    return pRet;
}

void fileWrite(const char* _pszFileName, void* _pImageData, int _Length)
{
    FILE* f = fopen(_pszFileName, "wb");
    fwrite(_pImageData, 1, _Length, f);
    fclose(f);
}

void _fileFree(void* _pData, const void* _pReadData, size_t _Size)
{
    free(_pData);
}



CImage::CImage(const char* _pszFileName)
{
    m_aPixels = NULL;
    LoadSDL( _pszFileName );
}

CImage::CImage(unsigned char* _aPixels, int _w, int _h)
{
    m_Width = _w;
    m_Height = _h;

    int size = _w*_h*4;
    m_aPixels = new unsigned char[size];
    
    memcpy(m_aPixels, _aPixels, size);
}

CImage::~CImage()
{
    delete[] m_aPixels;
    m_aPixels = NULL;
}

unsigned int CImage::GetRGBA(SDL_Surface* _pSurface, int _x, int _y)
{
    SDL_PixelFormat* pFormat = _pSurface->format;
    int readofs = (_y * (_pSurface->w)) + _x;
    int r,g,b,a;
    if( _pSurface->format->BytesPerPixel == 2 )
    {
        unsigned short* pData = (unsigned short*)_pSurface->pixels;
        r = ((pData[readofs]&pFormat->Rmask) >> pFormat->Rshift);
        g = ((pData[readofs]&pFormat->Gmask) >> pFormat->Gshift);
        b = ((pData[readofs]&pFormat->Bmask) >> pFormat->Bshift);
        r *= (255/15);
        g *= (255/15);
        b *= (255/15);
        
        if( pFormat->Amask )
        {
            a = ((pData[readofs]&pFormat->Amask) >> pFormat->Ashift);
            a *= (255/15);
        } else
        {
            a = 255;
        }
    }
    else if( _pSurface->format->BytesPerPixel == 4 )
    {
        unsigned int* pData = (unsigned int*)_pSurface->pixels;
        r = ((pData[readofs]&pFormat->Rmask) >> pFormat->Rshift);
        g = ((pData[readofs]&pFormat->Gmask) >> pFormat->Gshift);
        b = ((pData[readofs]&pFormat->Bmask) >> pFormat->Bshift);
        if( pFormat->Amask )
        {
            a = ((pData[readofs]&pFormat->Amask) >> pFormat->Ashift);
        } else {
            a = 255;
        }
    } else
    {
        unsigned char* pData = (unsigned char*)_pSurface->pixels;
        r=pData[readofs*4+0];
        g=pData[readofs*4+1];
        b=pData[readofs*4+2];
        a=pData[readofs*4+3];
    }

    unsigned int ret = (r<<24) + (g<<16) + (b<<8) + a;
    return ret;
}

void CImage::LoadSDL(const char* _pszFileName)
{
    m_pszFileName = strdup(_pszFileName);
    SDL_Surface* pSurface = IMG_Load(_pszFileName);
    if( pSurface == NULL )
    {
        printf("Failed to load texture '%s'\n", _pszFileName);
        return;
    }

    int src_width = pSurface->w;
    int src_height = pSurface->h;

    m_Width = src_width;
    m_Height = src_height;
    
    // Allocated memory needed for the bitmap context
    int texelsSize = m_Width * m_Height * 4;
    m_aPixels = new unsigned char[texelsSize];
    memset(m_aPixels, 0, texelsSize);

    // Convert image to texture
    int x, y;
    for( y=0; y<src_height; y++ )
    {
        for( x=0; x<src_width; x++ )
        {
            unsigned int rgba = GetRGBA(pSurface, x, y);
            int writeofs = (y*m_Width)+x;
            writeofs *= 4;    // 4 bytes per pixel in all of our textures
            m_aPixels[writeofs++] = (rgba>>24) & 0xff;
            m_aPixels[writeofs++] = (rgba>>16) & 0xff;
            m_aPixels[writeofs++] = (rgba>>8) & 0xff;
            int alpha = (rgba) & 0xff;
            m_aPixels[writeofs++] = alpha;
            //int a = (alpha & 0x80) != 0;
            //m_aCollisionMask[collwriteofs] = a;
        }
    }

    //
    SDL_FreeSurface(pSurface);
}

void CImage::SaveRAW(const char* _pszFileName)
{
    FILE* f = fopen(_pszFileName, "w");
    fwrite(m_aPixels, 1, m_Width*m_Height*4, f);
    fclose(f);
}

That image reader have a hack that I’m not proud of. The images that SDL read for me all had bits per pixel, bytes per pixel and the palette set to 0, so in the GetRGBA() method I simply just read 32 bpp RGBA colours if SDL doesn’t report a specific format. Because you know .. this isn’t what I want to spend time on. :)

Timers and frame rate

Yesterday I wanted to try out if it was possible to simulate different levels of grayscale on the screen by using the trick Nintendo used to use to achieve semi transparency on NES games.

The trick is simple. On even frames the pixel should be on, and on odd frames the pixel should be off. If you look at games like Metroid on the NES, when you spawn in at the very beginning Samus’ sprite is semi transparent. It is achieved by turning the sprite on and off every other frame. The TV screen, being unresponsive and interlaced is trying to keep up with what the hardware is trying to show, but it can’t and in the end the sprite comes out semi transparent.

When I did an earlier test I wrote a scrolling text and I noticed that the screen was quite unresponsive. When pixels where fading from on to off was clearly visible. So I figured that if I would flicker the pixel on and off the screen, being so unresponsive, should end up stuck in the fade between on and off.

So I wrote this little test

vmthread  MAIN
{
  DATA8 Flag
  DATA8 Draw_1of2

  MOVE8_8(0, Draw_1of2)

Loop:
  UI_DRAW( FILLWINDOW,0x00,0,0 )                  // Clear screen
  UI_DRAW( FILLRECT, FG_COLOR, 0, 0, 10, 10)      // Draw reference rect, fully opaque


  //
  // Draw 1 of 2
  //
  JR_NEQ8( Draw_1of2, 0, DrawDone_1of2 )          // if( Draw_1of2 != 0 )
  UI_DRAW( FILLRECT, FG_COLOR, 10, 0, 10, 10)     //     Draw full rect (every second frame)
DrawDone_1of2:
  ADD8( Draw_1of2, 1, Draw_1of2 )                 //
  JR_LT8( Draw_1of2, 2, Done_1of2 )               // if( Draw_1of2 >= 2 )
  MOVE8_8( 0, Draw_1of2 )                         //     Draw_1of2 = 0
Done_1of2:
  UI_DRAW( UPDATE )                               //  Show the stuff

  // Check for key to exit
  UI_BUTTON( SHORTPRESS, BACK_BUTTON, Flag )      // IF backbutton pressed THEN Flag=1
  JR_FALSE( Flag, Loop )                          // IF Flag==1 then JR Loop
}

But instead of seeing a semi transparent rectangle I saw a rectangle that was flickering out of control. The interval of the flickering wasn’t steady but seemed to vary. Some intervals the rectangle was shown multiple frames and some intervals it was only shown a single frame. It is obviously quite difficult to see exactly what is going on and for how long the rect was shown and hidden, but I could tell it was not flickering at a fixed and steady interval.

There where some timing issues.

I wanted the app to run in 60 FPS (old habit) and I didn’t know if my app was running faster or slower than that. In fact, I don’t even know the refresh rate of the screen. Now that I think about it, I guess what I really want to do is to sync my app with the refresh rate of the screen.

In any case, I wanted to take control of the timing of my app, so I looked into the TIMER functions.

The timer function would let me setup a timer and then stall the app until the timer had been reached. So I could say that, at this point in my code I want a timer that runs no longer than X milliseconds, and at another point in the code I could ask to wait for the timer to catch up with X.

So I added this:

vmthread  MAIN
{
  DATA8 Flag
  DATA8 Draw_1of2
  DATA32  Timer

  MOVE8_8(0, Draw_1of2)

Loop:
  TIMER_WAIT( 16, Timer )                         // Aim for 60 FPS

  UI_DRAW( FILLWINDOW,0x00,0,0 )                  // Clear screen
  UI_DRAW( FILLRECT, FG_COLOR, 0, 0, 10, 10)      // Draw reference rect, fully opaque


  //
  // Draw 1 of 2
  //
  JR_NEQ8( Draw_1of2, 0, DrawDone_1of2 )          // if( Draw_1of2 != 0 )
  UI_DRAW( FILLRECT, FG_COLOR, 10, 0, 10, 10)     //     Draw full rect (every second frame)
DrawDone_1of2:
  ADD8( Draw_1of2, 1, Draw_1of2 )                 //
  JR_LT8( Draw_1of2, 2, Done_1of2 )               // if( Draw_1of2 >= 2 )
  MOVE8_8( 0, Draw_1of2 )                         //     Draw_1of2 = 0
Done_1of2:
  UI_DRAW( UPDATE )                               //  Show the stuff

  TIMER_READY( Timer )                            // Wait for 60 FPS

  // Check for key to exit
  UI_BUTTON( SHORTPRESS, BACK_BUTTON, Flag )      // IF backbutton pressed THEN Flag=1
  JR_FALSE( Flag, Loop )                          // IF Flag==1 then JR Loop
}

In the added code the TIMER_WAIT( 16, Timer ) will store the current time + 16 in the variable Timer and TIMER_READY( Timer ) will wait until the current time have reached Timer. Like so:

/*! \page cTimer Timer
 *  <hr size="1"/>
 *  <b>     opTIMER_WAIT (TIME, TIMER)  </b>
 *
 *- Setup timer to wait TIME mS\n
 *- Dispatch status unchanged
 *
 *  \param  (DATA32)  TIME    - Time to wait [mS]
 *  \param  (DATA32)  TIMER   - Variable used for timing
 */
/*! \brief  opTIMER_WAIT byte code
 *
 */
void      cTimerWait(void)
{
  ULONG   Time;

  Time  =  *(ULONG*)PrimParPointer();

  *(ULONG*)PrimParPointer()  =  cTimerGetmS() + Time;
}


/*! \page cTimer
 *  <hr size="1"/>
 *  <b>     opTIMER_READY (TIMER) </b>
 *
 *- Wait for timer ready (wait for timeout)\n
 *- Dispatch status can change to BUSYBREAK
 *
 *  \param  (DATA32)  TIMER   - Variable used for timing
 */
/*! \brief  opTIMER_READY byte code
 *
 */
void      cTimerReady(void)
{
  IP      TmpIp;
  DSPSTAT DspStat = BUSYBREAK;

  TmpIp   =  GetObjectIp();

  if (*(ULONG*)PrimParPointer() <= cTimerGetmS())
  {
    DspStat  =  NOBREAK;
  }
  if (DspStat == BUSYBREAK)
  { // Rewind IP

    SetObjectIp(TmpIp - 1);
  }
  SetDispatchStatus(DspStat);

}

But even with that code the flickering wasn’t smooth 60 FPS. So I played around with the desired frame rate. I only achieved a stead flickering when I got as far down as 10 FPS. And at 10 FPS the flickering didn’t look like semi transparency, it was just flickering.

I guess at least that makes it easy for my future projects, because since I won’t be able to have semi transparency or gradients I won’t have to care about hitting a solid 60 FPS for my projects. :)

SSH (again)

There is a way to start an SSH server on the Brick, simply by executing
/etc/init.d/dropbear start

So now I can scp files to and from the Brick, and I can cat files on the Brick so I can easily

And that’s it for today.

scp

I was hoping I could use scp to copy files from my computer to the Brick but it turns out that scp is based on ssh. So that’s one difference between telnet and ssh. :)

Also, I was cocky and killed and restarted the lms2012 process on my Brick via telnet and now the Brick won’t shutdown. When I started lms2012 again it was like my prompt was waiting for the new process. When I tried to shut down the Brick via LMS2012 on the Brick I got a few error messages in the telnet prompt and a message about something being saved, and then nothing happened. It seemed like the Brick got stuck somewhere. I had to press the back and the middle button to reboot the Brick.

Anyhow, Brick is up in wifi and I can connect to it through Telnet. Happy and excited!

Wi-Fi pt. 2

Well .. that was easy.

Last login: Mon Aug 11 22:13:18 on ttys001
Magnus-starke-dator:~ magnusrunesson$ ssh root@192.168.1.246
ssh: connect to host 192.168.1.246 port 22: Connection refused
Magnus-starke-dator:~ magnusrunesson$ ping 192.168.1.246
PING 192.168.1.246 (192.168.1.246): 56 data bytes
64 bytes from 192.168.1.246: icmp_seq=0 ttl=64 time=3.038 ms
64 bytes from 192.168.1.246: icmp_seq=1 ttl=64 time=3.213 ms
^C
--- 192.168.1.246 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 3.038/3.125/3.213/0.088 ms
Magnus-starke-dator:~ magnusrunesson$ telnet 192.168.1.246
Trying 192.168.1.246...
Connected to 192.168.1.246.
Escape character is '^]'.

 _____       _     _ ___
|  _  |_ _ _| |___| | __|
|    _| | | . | . | | _|
|__|__|___|___|___|_|_|

Rudolf 2011.01 EV3

login: root
root@EV3:~# ls -la
drwxr-xr-x    1 root     root          108 Jan  1 00:00 lms2012
root@EV3:~# cwd
-sh: cwd: not found
root@EV3:~# 

So, not SSH but Telnet. I don’t even know the difference so what do I care, I’m on the Brick! :)

UPDATE! Having wi-fi drain battery like nobodies business. I went from almost full to almost empty in a couple of minutes. Hmm.. I think my next purchase/project will be something to replace the batteries. :)

Transferring files to and from the Brick

So far I have been using LabVIEW to transfer files to and from my Brick. Besides swapping the SD card between the Brick and my computer that was the only way I knew of that would work without creating a special cable or getting a wifi dongle. (Which I have ordered but it has not yet appeared. Cursed you Webhallen! )

Yesterday I was poked regarding MonoBrick and I had a look at it, and what does it provide if not a way to communicate with the Brick over Bluetooth! The MonoBrick Communications Library is a C# library that allow you to create an app in C# that can communicate with a EV3 or NXT Brick.

Excited about the possibilities I downloaded the DLL’s and imported them in my favorite C# environment, Unity. Unfortunately Unity isn’t the best C# environment as it only supports .NET 2.0, and it turns out the MonoBrick Communications Library require a higher .NET version. I don’t know which one but it works with .NET 4.0 at least. (I use Unity at work everyday and I still don’t really know my way around the .NET environment and all the different versions of .NET and Mono etc.. Sweet ignorance!)

So after realizing that MonoBrick doesn’t work with Unity I decided to try and build a stand alone app with MonoDevelop and it sort of worked. I booted up the MonoDevelop that comes with Unity and created a solution that built beautifully. But it wouldn’t start. The app was set to build to x86, and there was no way to change to build to Mac. (Who BTW use Intel Core i7 which are x86 compatible, so when MonoDevelop say x86 it doesn’t mean it will build something that run on any x86, MonoDevelop mean it will build a Windows executable)

Since I didn’t find a way to switch to build for OSX I decided to download Xamarin Studio and Mono MDK, but once those where downloaded and installed I could finally have another go at the sample apps that comes with MonoBrick.

But no!

After loading the sample apps I realize the project files are made specifically for x86 (i.e. Windows) and there is no way to switch target platform once it has been set. Seriously! What is the point of installing a hardware abstraction layer like Mono if I can’t build apps that run in Mono on all platforms that Mono supports? I’d be super happy if someone who knows this stuff could fill me in!

In any case, I decided to create a completely new project and chose that it should be targeted towards Mac. When doing that I couldn’t find a way to create a console app for OSX, all I found was a template that looks a lot like the template app you get with Xcode. All the classes and files are the same. Except these where written in C#.

Anyhoo, now I had a C# app up and running on my computer and I started to copy the code from the MonoBrick sample project, and I ran into the next problem. The MonoBrick namespace wasn’t recognized. I had added the MonoBrick DLL into my project by simply adding all files from the downloaded MonoBrick package, but for some reason Xamarin Studio didn’t seem to parse the DLL’s when doing that, so what I ended up doing was right click on the “References” folder in my solution and chose “Edit references…”. I went to the “.Net Assembly” tab which seemed to be some form of file browser. I could highlight MonoBrick.dll and LibUsbDotNet.dll and click “Add” and they where properly added and parsed into my project, and now the sample program started.

And now, my lovely friends, the app connected to the Brick! Happy fun time! My first program was this:

var brick = new Brick<Sensor,Sensor,Sensor,Sensor>("/dev/tty.EV3-SerialPort");
brick.Connection.Open();
BrickFile[] files;
string[] folders;
brick.FileSystem.GetFolderInfo( ".", out files, out folders );

Console.WriteLine( "Files" );
foreach( BrickFile file in files )
	Console.WriteLine( "File: " + file.FullName );

Console.WriteLine( "Folders" );
foreach( string folder in folders )
	Console.WriteLine( "Folder: " + folder );

I don’t remember what the output was but I was listing some folder somewhere on the Brick. The joy and excited I had over this is indescribable! This could mean I have another way of synching my app to the Brick so I don’t have to go through LabVIEW!

After that I spent a good amount of time browsing the files on the Brick by passing in different folders. First I looked at “/”, and then I found my way to “/mnt/card/” and “/home/root/lms2012/” and I had a generally good feeling about it. Sometimes I had the issue of files showing up in the memory browser on the Brick but not when I list files over bluetooth. I still don’t know why and most of the time it seemed to work so I haven’t bothered looking into it.

After that I attempted to poke the Brick, not just peek into it. So I added this:

brick.FileSystem.CreateDirectory( "/mnt/card/bluetooth/" );

And it worked! When listing files in “/mnt/card/” I could see there was a bluetooth folder, and when using the memory browser on the brick the bluetooth folder was there as well. Next I added this:

brick.FileSystem.WriteFile( "/Users/magnusrunesson/Projects/ev3zipper/log.rbf", "/mnt/card/bluetooth/log.rbf" );

And I ran into problems! I got a “permission denied” error message. I suspected there was problems accessing the local file, I was thinking that perhaps there was a security thing going on with Mono not being allowed to access any ol’ file on my drive, so I removed the brick.FileSystem.WriteFile() call and used System.IO.File to open the same local file, and when trying the app again I got the same permission denied error message.

Now I was sure that it was a Mono problem, but then I noticed that the brick.FileSystem.CreateDirectory() call was still there, so I removed it, put the CreateFile back in and tried again, and this time it worked beautifully to create a file. I had transferred my first RBF file to the Brick without using LabVIEW. It worked fine to start the RBF on the Brick using the memory browser. The program generated a log file that for some reason didn’t show up in the memory browser on the Brick, but when listing files remotely it did show up.

The next step was to try and download the log file from Brick to my computer, by using this piece of code:

brick.FileSystem.ReadFile( "/mnt/card/bluetooth/log.txt", "/Users/magnusrunesson/Projects/ev3zipper/megabluetoothlog.txt" );

And again that worked like a charm. So now I have a C# app that at it’s very basic core can upload files to the Brick and download files from the Brick.

With the C# FileSystemWatcher class I’m hoping to have a way of automatically assembling and downloading my app to the Brick as I work with it, so when the FileSystemWatcher notice a change in my LMS file it could automatically assemble it into an RBF file and transfer it to the Brick. That way all I have to do is press Save in my text editor (which is Sublime btw, it kicks ass) wait a few seconds and then start the app on the Brick. Or even automate starting the app, MonoBrick seems to support starting programs remotely through the brick.StartProgram() function. It could be extended so the C# app also notices changes in source assets like graphics and automatically convert a source BMP file to RGF, for example. Same with sound.

A downside to this though seems to be battery usage. I’ve been using the Brick quite extensively for the last week, but only connected via USB, and the batteries have been quite charged still, but after a few hours of being connected with bluetooth I can actually see a drop in battery charge. So I am a bit concerned that bluetooth use a bit too much batteries. But then on the other hand, MonoBrick does support connection over USB so if that use less battery then yay, go USB!