lunedì 13 giugno 2016

LCD1602 su PIC DevBoard

Codice, ripreso da un produttore cinese, su come usare LCD1602 con PIC. Compilatore MapLab IDE





Schema elettrico


main.c
---------------------------------------------
#include <pic.h>          
#include "LCD1602.h"         
//---------------------------------------
unsigned char str0[]={"Luca            "};
unsigned char str1[]={"Innocenti       "};
unsigned char str2[]={"debiaonoldcomput"};
unsigned char str3[]={"ers.blogspot.com"};
//---------------------------------------

void main(void)           
{

    LCD1602_GPIO_Init();
    LCD1602_init();      
  

    while(1)              
    {
    DisplayListChar(0,0,str0);   
    DisplayListChar(0,1,str1);
  
    Delay1602_MS(500);
 
    DisplayListChar(0,0,str2);   
    DisplayListChar(0,1,str3);
    Delay1602_MS(500);
    }
}

---------------------------------------------


 LCD1602.H
---------------------------------------------
#ifndef  __LCD1602_H
#define  __LCD1602_H


#define E   RA3           
#define RW  RA2        
#define RS  RA5         
#define busy RD7         
#define busy_init TRISD7  
#define Lcd_Date PORTD    


void Delay1602_US(unsigned int t);
void Delay1602_MS(unsigned int t);
void LCD1602_BusyTest(void);
void LCD1602_Write_Instruction(unsigned char combuf);
void LCD1602_Write_data_busy(unsigned char databuf);
void LCD1602_init(void);
void DisplayOneChar(unsigned char X,unsigned char Y,unsigned char DData);
void DisplayListChar(unsigned char X,unsigned char Y,unsigned char *DData);
void LCD1602_GPIO_Init(void);


#endif

---------------------------------------------


 1602.c
---------------------------------------------
#include <pic.h>        
#include "LCD1602.h"   
__CONFIG(HS&WDTDIS&LVPDIS);




void Delay1602_US(unsigned int t)
{
     unsigned int k;     
     for(k=0;k<t;k++);   
}

void Delay1602_MS(unsigned int t)
{
     while(t--)
     Delay1602_US(200);
}
void LCD1602_BusyTest(void)
{
     busy_init=1;          
     RS=0;               
     RW=1;              
     E=1;                
     asm("NOP");         
     asm("NOP");         
     while(busy==1);      
     E=0;                
     busy_init=0;         
}


void LCD1602_Write_Instruction(unsigned char combuf)
{
     LCD1602_BusyTest();         
     RS=0;              
     RW=0;               
     E=0; 
     asm("NOP");         
     Lcd_Date=combuf;      
     asm("NOP");         
     asm("NOP");         
     E=1;              
     asm("NOP");         
     E=0;                
}


void LCD1602_Write_data_busy(unsigned char databuf)
{
     LCD1602_BusyTest();  
     RS=1;              
     RW=0;               
      E=0; 
     asm("NOP");         
     Lcd_Date=databuf;       
     asm("NOP");         
     asm("NOP");         
     E=1;                
     asm("NOP");        
     E=0;                
}



void LCD1602_init(void)
{
     Delay1602_US(1500);         
     LCD1602_Write_Instruction(0x38);
     Delay1602_US(500);         
     LCD1602_Write_Instruction(0x38);
     Delay1602_US(500);          
     LCD1602_Write_Instruction(0x38); 
     LCD1602_Write_Instruction(0x38); 
     LCD1602_Write_Instruction(0x08); 
     LCD1602_Write_Instruction(0x01); 
     LCD1602_Write_Instruction(0x06); 
     LCD1602_Write_Instruction(0x0C);
}



void DisplayOneChar(unsigned char X,unsigned char Y,unsigned char DData)
{
    Y&=1;
    X&=15;
    if(Y)X|=0x40;             
    X|=0x80;                  
    LCD1602_Write_Instruction(X);
    LCD1602_Write_data_busy(DData);
}

void DisplayListChar(unsigned char X,unsigned char Y,unsigned char *DData)
{
    unsigned char ListLength=0;
    Y&=0x01;
    X&=0x0f;
    while(X<16)
    {
        DisplayOneChar(X,Y,DData[ListLength]);
        ListLength++;
        X++;
    }
}


void LCD1602_GPIO_Init(void)
{
    ADCON1=0X07;
    TRISA=0B11010011;    
    TRISD=0B00000000;    
    PORTA=0B00000000;    
    PORTD=0B00000000;    
}

---------------------------------------------

Emulatori 6502 su Arduino

Andando a giro su Internet ho scoperto che esiste la possibilta' di emulare il glorioso processore MOS 6502 (e la sua variante MOS 6510) su Arduino.
Esistono diversi progetti come quello per il C64, per il VIC 20 ed il KIM-1.

Per emulare il C64 e' necessario utilizzare una Arduino Due a causa della mancanza di memoria della scheda Uno



Una volta caricato il programma (da qui) ci si deve collegare via seriale ma non usando il terminale seriale di Arduino IDE (Minicom e Hyperterminal funzionano, configurazione 9600 8N1). Ci si trova davanti all'interprete BASIC


a parte lo spazio di memoria libera veramente esigua il sistema funziona

Un po' (un bel po') piu' impegnativo e' usare l'emulatore del KIM-1 (funzionante anche su Arduino Uno e reperibile a questo indirizzo) perche' di fatto il sistema si programma direttamente in Linguaggio Macchina (non e' un errore, non si programma in Assembler, si inputa il codice direttamente in LM)
Come nel caso precedente si deve usare un terminale seriale esterno alla IDE di Arduino



La cosa piu' divertente e' pero' dare un'occhiata al file cpu.c dove e' contenuta la divisione della memoria con eventuali programmi caricati come ROM, la traduzione delle istruzioni in Assembler 6502 comprese quelle non documentate e  la gestione degli interrupt

giovedì 9 giugno 2016

RealTime Operating Systems RTOS per Arduino

Una delle cose belle di Arduino e' che, pur nella sua semplicita', esegue in modo ripetibile e prevedibile un compito...ma in fondo e' uno solo. Se il sistema e' intrappolato, per esempio in un loop infinito, esistono sistemi basati sulla gestione degli interrupt per venire incontro a condizioni non prevedbili, come per esempio la pressione di un tasto da parte dell'utente

Un sistema decisamente piu' evoluto per gestire processi concorrenti su un processore da risorse cosi' ridotte come l'AVR ATMega328 e' quello di usare i sistemi operativi realtime, in breve RTOS (sistema operativo inteso come kernel ed API, non come interfaccia utente)

Con questi software si puo' avere la gestione di thread concorrenti con gestione della priorita' dei task, la gestione di semafori Mutex., binari o counting semaphore per sincronizzare i processi. Una caratteristica fondamentale dei sistemi RTOS e' quello di eseguire un task in un arco temporale (minimo e massimo) ben definito; se dopo il tempo massimo il task non ha finito il compito deve lasciare spazio agli altri task concorrenti. Questa caratteristica e' importante per sistemi robotizzati: per esempio se un automa si muove e contemporaneamente acquisisce dati, i sensori di urto devono avere la priorita' sull'acquisizione e non ci deve essere ritardo tra quando viene lanciato l'allarme di prossimita' e la conseguente azione. Su Linux (sempre che non monti un kernel modificato per essere realtime..la Nasa all'inizio degli anni 2000 aveva creato Flight Linux) puo' succedere che il sistema sia occupata in un task lungo e non rilasci in tempo le risorse per evitare l'urto del robot

Per Arduino esistono sostanzialmente FreeRTOS e ChibiOS

Come documentazione Chibios e' decisamente meglio documentato , FreeRTOS ha un libro a pagamento mentre nel file .zip con il sistema operativo  c'e' una cartella con gli esempi ma non relativi ad ATMega.

Un caso pratico in cui si puo' capire il vantaggio di un sistema RTOS della classica programmazione Arduino e' quello in cui si gestisce un flusso seriale ed in maniera indipendente si devono gestire anche l'acquisizione dei sensori


FreeRTOS
Per installare le libreriei di FreeRTOS nella IDE di Arduino e' sufficiente andare nel menu Sketch/Includi libreria/Gestione Librerie e cercare ed installare FreeRTOS. Stranamente non vengono installati degli esempi




ChibiOS
Per installare ChibiOS in Arduino IDE si parte scaricando le librerie da questo link.(esiste un sistema di sviluppo completo costituito da ChibiOS Studio ma e' possibile compilare per ATMega ma solo per ARM STM32) e si pone la sottodirectory /libraries/ChibiOS_AVR presente nel file zip dentro la directory libraries della IDE di Arduino (sotto Windows si trova nella propria C:\Documents and Settings\xxxxx\Documenti\Arduino\libraries\ChibiOS_AVR mentre in Linux e' semplicemente in libraries)

ChibiOS Studio

Dal punto di vista di scrittura del codice ci sono diversi similitudini. Prendendo due esempi semplici (riportati piu' in basso). Nella parte di setup vengono dichiarati i task che fanno riferimenti a due funzioni con l'indicazione della priorita'; all'interno della funzione del task sono riportate le azioni di ciascun thread FreeRTOS usa il termine task che corrisponde al thread di Chibios)

Esempio FreeRTOS. 
Due Task in cui uno lampeggia un led e l'altro legge i dati da una porta analogica e lo spedisce sulla seriale
---------------------------
#include <Arduino_FreeRTOS.h>
#include <croutine.h>
#include <event_groups.h>
#include <FreeRTOSConfig.h>
#include <FreeRTOSVariant.h>
#include <list.h>
#include <mpu_wrappers.h>
#include <portable.h>
#include <portmacro.h>
#include <projdefs.h>
#include <queue.h>
#include <semphr.h>
#include <StackMacros.h>
#include <task.h>
#include <timers.h>

#include <Arduino_FreeRTOS.h>

// define two tasks for Blink & AnalogRead
void TaskBlink( void *pvParameters );
void TaskAnalogRead( void *pvParameters );

// the setup function runs once when you press reset or power the board
void setup() {

  // Now set up two tasks to run independently.
  xTaskCreate(
    TaskBlink
    ,  (const portCHAR *)"Blink"   // A name just for humans
    ,  128  // Stack size
    ,  NULL
    ,  2  // priority
    ,  NULL );

  xTaskCreate(
    TaskAnalogRead
    ,  (const portCHAR *) "AnalogRead"
    ,  128 // This stack size can be checked & adjusted by reading Highwater
    ,  NULL
    ,  1  // priority
    ,  NULL );

  // Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
}

void loop()
{
  // Empty. Things are done in Tasks.
}

/*--------------------------------------------------*/
/*---------------------- Tasks ---------------------*/
/*--------------------------------------------------*/

void TaskBlink(void *pvParameters)  // This is a task.
{
  (void) pvParameters;

  // initialize digital pin 13 as an output.
  pinMode(13, OUTPUT);

  for (;;) // A Task shall never return or exit.
  {
    digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
    digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
    vTaskDelay( 1000 / portTICK_PERIOD_MS ); // wait for one second
  }
}

void TaskAnalogRead(void *pvParameters)  // This is a task.
{
  (void) pvParameters;

  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  for (;;)
  {
    // read the input on analog pin 0:
    int sensorValue = analogRead(A0);
    // print out the value you read:
    Serial.println(sensorValue);
    vTaskDelay(1);  // one tick delay (15ms) in between reads for stability
  }
}
---------------------------

Esempio ChibiOs 
Due Task che fanno lampeggiare il led con l'uso di un semaforo per scambiarsi i dati tra i due thread
----------------------------
// Example to demonstrate thread definition, semaphores, and thread sleep.
#include <ChibiOS_AVR.h>

// The LED is attached to pin 13 on Arduino.
const uint8_t LED_PIN = 13;

// Declare a semaphore with an inital counter value of zero.
SEMAPHORE_DECL(sem, 0);
//------------------------------------------------------------------------------
// Thread 1, turn the LED off when signalled by thread 2.

// 64 byte stack beyond task switch and interrupt needs
static THD_WORKING_AREA(waThread1, 64);

static THD_FUNCTION(Thread1, arg) {

  while (!chThdShouldTerminateX()) {
    // Wait for signal from thread 2.
    chSemWait(&sem);

    // Turn LED off.
    digitalWrite(LED_PIN, LOW);
  }
}
//------------------------------------------------------------------------------
// Thread 2, turn the LED on and signal thread 1 to turn the LED off.

// 64 byte stack beyond task switch and interrupt needs
static THD_WORKING_AREA(waThread2, 64);

static THD_FUNCTION(Thread2, arg) {
  pinMode(LED_PIN, OUTPUT);
  while (1) {
    digitalWrite(LED_PIN, HIGH);

    // Sleep for 200 milliseconds.
    chThdSleepMilliseconds(200);

    // Signal thread 1 to turn LED off.
    chSemSignal(&sem);

    // Sleep for 200 milliseconds.
    chThdSleepMilliseconds(200);
  }
}
//------------------------------------------------------------------------------
void setup() {

  chBegin(chSetup);
  // chBegin never returns, main thread continues with mainThread()
  while(1) {
  }
}
//------------------------------------------------------------------------------
// main thread runs at NORMALPRIO
void chSetup() {

  // start blink thread
  chThdCreateStatic(waThread1, sizeof(waThread1),
    NORMALPRIO + 2, Thread1, NULL);

  chThdCreateStatic(waThread2, sizeof(waThread2),
    NORMALPRIO + 1, Thread2, NULL);

}
//------------------------------------------------------------------------------
void loop() {
  // not used
}




mercoledì 8 giugno 2016

Windows XP e Dropbox (API)

Il prossimo 29 agosto Dropbox terminera' il supporto per il client Windows XP.
Al contrario di come fanno altre ditte, per esempio Google con Chrome, la fine del supporto non indica che non saranno piu' sviluppati aggiornamenti ma che il client terminera' il suo funzionamento (sara' sempre disponibile l'interfaccia web)

Considerando che Dropbox e' di proprieta' Microsoft e che il servizio e' fondamentalmente gratuito non e' da biasimare Microsoft se cerca di piazzare qualche SO nuovo...ma forse una soluzione semplice esiste ed e' quella di usare un client Python usanto le API dell'SDK di Dropbox




Per usare il client Python ci si deve prima registrare su Dropbox Developer, creare una App (la piu' semplice e' App Folder) e generare un Access Token

Successivamente si scaricare l'SDK Python di Dropbox da questo indirizzo https://github.com/dropbox/dropbox-sdk-python installandolo poi con il classico

python setup.py install

a questo si puo' modificare il programma updown.py che permette di sincronizzare una cartella (cosi' come fa il client). Per personalizzare la directory da sincronizzare si deve modificare la rootdir (di default e' Downloads) e la cartella remota di Dropbox.

il progrramma si lancia quindi con la sintassi (il token si deve copiare dalle impostazioni della app)

python updown.py --token xxxxxxxxxxxxxxxxxxxxxxxxxxxxx --yes 

(lo yes finale risponde in modo affermativo alle domande in modo da sovrascrivere in modo automatico)


i file sincronizzati non si trovano nella radice di Dropbox ma nella sottodirectory /App/nomedellaApp








Mandelbrot su C64 in Assembler

Frugando su internet ho trovato la versione in Assembler di Mandelbrot per C64 compresa nel progetto Resurrection (sotto cartella Mandelbrot)

Il codice e' ben commentato ed il risultato e' in modalita' testo



Il tempo di calcolo su VICE in emulazione del tempo reale del C64 risulta essere di 90 secondi con 26 iterazioni per punto
Il codice e' compilabile con il crosscompiler ACME e necessita della libreria stdlib
----------------------------------------------------------------
;Mandelbrot test code

!source "../stdlib/stdlib.a"
!to "Mandelbrot.prg", cbm
!sal
!sl "Mandelbrot.map"
!svl "Mandelbrot.lbl"
!cpu 6510
!ct pet

!source "../stdlib/BASICEntry80d.a"

!macro OutputFAC {
jsr $bddd ; Convert FAC#1 to ASCII String. Kills FAC#2
jsr $b487 ; Set Up String
jsr $ab21 ; Output String
}

LDFAC = $bba2
STFAC = $bbd4
SUBFAC = $b850
DIVFAC = $bb0f
CMPFAC = $bc5b
MULFAC = $ba28
ADDFAC = $b867
SETFAC = $b391

!macro LoadFAC .addr {
lda #<.addr
ldy #>.addr
jsr LDFAC
}

!macro StoreFAC .addr {
ldx #<.addr
ldy #>.addr
jsr STFAC
}

!macro SubMemFAC .addr {
lda #<.addr
ldy #>.addr
jsr SUBFAC
}
!macro SubFACMem .addr {
+StoreFAC Temp
+LoadFAC .addr
+SubMemFAC Temp
}

!macro DivMemFAC .addr {
lda #<.addr
ldy #>.addr
jsr DIVFAC
}
!macro DivFACMem .addr {
+StoreFAC Temp
+LoadFAC .addr
+DivMemFAC Temp
}

!macro CmpFACMem .addr {
+StoreFAC Temp
+LoadFAC .addr
+CmpMemFAC Temp
}
!macro CmpMemFAC .addr {
lda #<.addr
ldy #>.addr
jsr CMPFAC
}

!macro MulMemFAC .addr {
lda #<.addr
ldy #>.addr
jsr MULFAC
}

!macro AddMemFAC .addr {
lda #<.addr
ldy #>.addr
jsr ADDFAC
}

!macro SetFAC_Y {
lda #0
jsr SETFAC
}

kMaxIterations = 26

!zn
*=BASICEntry
cld
jsr CINT


; Speed testing for some FP ops. As we can see even some simple operations are very slow
!if 0 {
sei
.tl1
lda #0
sta VIC2BorderColour
+MACROWaitForRaster 64
inc VIC2BorderColour

+LoadFAC xmin
inc VIC2BorderColour

+SubMemFAC xmax
inc VIC2BorderColour

+DivFACMem ScrW
inc VIC2BorderColour

+StoreFAC xs
inc VIC2BorderColour

+MulMemFAC xs
inc VIC2BorderColour

+StoreFAC xs
inc VIC2BorderColour

jmp .tl1
}

; The main Mandelbrot code.
;xs=(xmax-xmin)/40.0;
+LoadFAC xmin
+SubMemFAC xmax
+DivFACMem ScrW
+StoreFAC xs

;ys=(ymax-ymin)/24.0;
+LoadFAC ymin
+SubMemFAC ymax
+DivFACMem ScrH
+StoreFAC ys

;for (y=0;y<24;y++) {
lda #0
sta y

.l1
;   for (x=0;x<40;x++) {
lda #0
sta x

.l2
;      p=xmin+(x*xs);
ldy x
+SetFAC_Y
+MulMemFAC xs
+AddMemFAC xmin
+StoreFAC p


;      q=ymin+(y*ys);
ldy y
+SetFAC_Y
+MulMemFAC ys
+AddMemFAC ymin
+StoreFAC q


;         xtemp=0;
;         x0=0;
;         y0=0;
ldy #0
+SetFAC_Y
+StoreFAC x0
+StoreFAC y0


;         i=0;
lda #0
sta i

.while
;         while (((x0*x0)+(y0*y0))<4 && ++i<kMaxIterations)  {
; i check, first, simple integer check
lda i
cmp #kMaxIterations
bcc .ltkMaxIterations

jmp .escape
.ltkMaxIterations

; mul check
+LoadFAC x0
+MulMemFAC x0
+StoreFAC Temp2
+LoadFAC y0
+MulMemFAC y0
+AddMemFAC Temp2
+CmpFACMem Escape
bcc .lt4
jmp .escape
.lt4

;++i
inc i

; xtemp = (x0*x0) - (y0*y0) + p
+LoadFAC x0
+MulMemFAC x0
+StoreFAC Temp2
+LoadFAC y0
+MulMemFAC y0
+SubMemFAC Temp2
+AddMemFAC p
+StoreFAC xtemp

;            y0=(2 * x0 * y0) +q;
+LoadFAC x0
+MulMemFAC y0
+StoreFAC Temp2
ldy #2
+SetFAC_Y
+MulMemFAC Temp2
+AddMemFAC q
+StoreFAC y0


;            x0=xtemp;
+LoadFAC xtemp
+StoreFAC x0

;            }
jmp .while

.escape
; Range check to adjust for printable characters
lda i
; lsr ; Potential for shit adjustment, change kMaxIterations to accomodate
cmp #26 ; Letters in the alphabet!
bcc .okChar
lda #' '
jmp .rawOut
.okChar
clc
adc #64
.rawOut
jsr CHROUT

; Finish for loops
inc x
lda x
cmp #40
beq .ol2
jmp .l2
.ol2
inc y
lda y
cmp #24 ; MPi: TOOD: Keep the last line clear for an IRQ scrolling message
beq .ol1
jmp .l1
.ol1

.flash
; inc VIC2BorderColour
jmp .flash

; C64 Floating point values
; MPi: TODO: Must update the assembler to create these
xmin !by $82, $a0, $00, $00, $00 ; -2.5
xmax !by $81, $40, $00, $00, $00 ; 1.5
ymin !by $81, $c0, $00, $00, $00 ; -1.5
ymax !by $81, $40, $00, $00, $00 ; 1.5
ScrW !by $86, $20, $00, $00, $00 ; 40
ScrH !by $85, $40, $00, $00, $00 ; 24
Escape !by $83, $00, $00, $00, $00 ; 4

; Float storage
Temp = *
Temp2 = * + 5
xs = * + 10
ys = * + 15
p = * + 20
q = * + 25
xtemp = * + 30
x0 = * + 35
y0 = * + 40
; Bytes
x = * + 45
y = * + 46
i = * + 47
----------------------------------------------------------------



IEEE 754

Si tratta di un metodo di memorizzazione di numeri float  con varia lunghezza nel numero di bit (in questo caso sara' utilizzata solo la rappresentazione a 32 bit). Un convertitore on line di numeri in formato IEEE754 puo' esssere trovato a questo link

I bit sono cosi' divisi
1 bit di segno
8 bit di esponente : 2 elevato all'esponente
23 bit : mantissa del numero

In questo formato i numeri 12.5 e 3.5 ottengono la seguente rappresentazione binaria

12.5  0 10000010 10010000000000000000000  1.5625   4718592
3.5    0 10000000 11000000000000000000000  1.75     6291456

il primo bit e' in entrambi i casi 0 perche' i due numeri sono positivi

12.5 puo' essere espresso come 2^3 *1.5625. L'esponente e' quindi 3 ma deve essere sommato il valore di 127 per cui l'esponente e' 127 + 3 = 130d =  10000010b. Infine 10010000000000000000000 e' la rappresentazione binaria di 1.5625

Lo stesso si puo' ripetere per il numero 3.5 che puo' essere espresso come 2^1*1.75

Per sommare i due numeri si procede allineando gli esponenti a quello piu' basso (in questo caso 1). 12.5 viene quindi espresso come 6.25*2^1 (spostando l'esponente di due si moltiplica la mantissa per quattro ovvero 2^spostamento). A questo punto si sommando le mantissa e la somma e' uguale a (6.25+1.75)*2^1 = 8*2=16 ovvero il risultato desiderato (12.5+3.5)

per moltiplicare i due numeri (12.5*3.5=43.75) si procede sommando gli esponenti e moltiplicando le mantisse. In questo caso 2^(1+3)*(1.5625*1.75)=2.734375*2^4. La mantissa deve essere normalizzata perche' e' superiore a 2 per cui il risultato sara' 1.3671875*2^5 ovvero 43.75


Perche' tanto confusione .. perche' ai computer piacciono i numeri a base due ed interi

Questa e' un sistema standardizzato ma esistono altri modi per descrivere un numero. In questo fantastico documento del 1976 a firma tale Roy Rankin e Steve Wozniak sulle procedure matematiche per il 6502 con una rappresentazione a 32 bit nel formato

 Exponent    Two's Complement Mantissa
  SEEEEEEE  SM.MMMMMM  MMMMMMMM  MMMMMMMM
     n         n+1       n+2       n+3

Repository per Yocto su Intel Edison

Come fatto notare in un precedente post, il repository di default opkg di Intel Edison non e' cosi' fornito cosi' si e' abituati per esempio con Arduino YUN.




La soluzione deriva dalle AlexT's Edison Page,   un repository non ufficiale che riporta un discreto numero di pacchetti di uso comune gia' in formato binario

Per abilitare questo repository si deve aggiungere al file /etc/opkg/base-feeds.conf le seguenti righe

-------------------------------
src/gz all http://repo.opkg.net/edison/repo/all
src/gz edison http://repo.opkg.net/edison/repo/edison
src/gz core2-32 http://repo.opkg.net/edison/repo/core2-32
-------------------------------
si digita quindi
opkg update

(sul sito viene esplicitamente indicato di non usare opkg upgrade ma di configurare l'upgrade pacchetto per pacchetto pena la saturazione della rootfs)
Si possono quindi installare pacchetti come opencv e motion senza fatica

Change Detection with structural similarity

L'idea di base e' quella di cercare le differenze tra le due immagini sottostanti Non e' immediatamente visibile ma ci sono dei ...