mercoledì 29 giugno 2016

Autostart Python e Sketch in Intel Edison

Gli sketch su Intel Edison, a differenza di Arduino, non vanno in esecuzione automatica.
Per fare cio si deve creare la directory /etc/init.d ed un file sh

mkdir /etc/init.d
cd /etc/init.d
vi automatico.sh


all'interno del file automatico.sh si digita il comando exec puntado al file sketch.elf che dovra' essere stato prima spostato dalla directory /tmp a /sketch
----------------------------
#!/bin/sh
exec /sketch/sketch.elf /dev/ttyGS0 /dev/ttyGS0

----------------------------
a questo punto per l'esecuzione automatica si rende il file eseguibile

root@edison:/etc/init.d# chmod +x /etc/init.d/automatico.sh
root@edison:/etc/init.d# update-rc.d automatico.sh defaults

lo stesso si puo' fare anche con gli script Python modificando il contenuto del file /etc/init.d/automatico.sh come segue
----------------------------
 #!/bin/sh
python /home/root/programma.py >> /dev/null 2>&1 &
----------------------------

Modulo Radio 433 MHz HM-TRP con SIK

Volevo provare a sostituire i moduli XBee con qualcosa di piu' perfomante, piu' che altro alla ricerca di allargare il raggio di trasmissione
Su consiglio di un amico mi sono comprato i trasmettitori 433 MHz 500 mA della Drotek che dovevano essere basati sul firmware open source Sik


Si tratta di moduli radio nati per essere montati sui droni ed hanno gia' i connettori predisposti per PixHawk ma di fatto sono solo dei trasmettitori che si comportano come delle seriali virtuali



Il dispositivo e' arrivato senza nessun tipo di documentazione (CD, Libro), sul sito non ci sono riferimenti alla documentazione. Ho provato a contattare direttamente Drotek e devo ammettere che questa ditta non ha assolutamente nessuna assistenza clienti (la prossima volta compro in Cina...tanto e' uguale a parte il prezzo)

Sulla base delle poche informazioni fornite dal mio amico mi sono rimboccato le maniche ed ho cercato di prendere il controllo del dispositivo.

Connesso il trasmettitore ad una CentOs 7 via microUsb il dispositivo compare come seriale virtuale.
------------------------------------------------
Jun 29 15:31:31 localhost kernel: usb 1-1.2: new full-speed USB device number 3 using ehci-pci
Jun 29 15:31:31 localhost kernel: usb 1-1.2: New USB device found, idVendor=10c4, idProduct=ea60
Jun 29 15:31:31 localhost kernel: usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
Jun 29 15:31:31 localhost kernel: usb 1-1.2: Product: CP2102 USB to UART Bridge Controller
Jun 29 15:31:31 localhost kernel: usb 1-1.2: Manufacturer: Silicon Labs
Jun 29 15:31:31 localhost kernel: usb 1-1.2: SerialNumber: 0001
Jun 29 15:31:31 localhost mtp-probe: checking bus 1, device 3: "/sys/devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2"
Jun 29 15:31:31 localhost mtp-probe: bus: 1, device: 3 was not an MTP device
Jun 29 15:31:32 localhost kernel: usbcore: registered new interface driver cp210x
Jun 29 15:31:32 localhost kernel: usbserial: USB Serial support registered for cp210x
Jun 29 15:31:32 localhost kernel: cp210x 1-1.2:1.0: cp210x converter detected
Jun 29 15:31:32 localhost kernel: usb 1-1.2: cp210x converter now attached to ttyUSB1

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

a questo punto rimane il problema di programmarlo e ricevere/inviare i dati
Se si cerca il firmware Sik si attiva a questa pagina su GitHub. Nella sottocartella Firmware/Tools ci sono dei comodi script Python ed in particolare leggendo il codice del programma show_regs.py sembra quello utile a capire se il dispositivo risponde

per fare funzionare questo script si devono soddisfare le dipendenze con PySerial (pip install pyserial) e Pexpect. Per questa seconda libreria ho avuto qualche problema con pip ed ho usato l'installazione standard partendo da http://pexpect.sourceforge.net/doc/#download ed installando con python setup.py install

A questo punto digitando
[root@localhost tools]# ./show_regs.py /dev/ttyUSB1

si hanno le seguenti risposte
------------------------------------------------
showing registers on /dev/ttyUSB1
+++
ATI
OK
ATI5

ATI
SiK 1.9 on HM-TRP
ATI5
S0:FORMAT=25
S1:SERIAL_SPEED=57
S2:AIR_SPEED=64
S3:NETID=25
S4:TXPOWER=11
S5:ECC=1
S6:MAVLINK=1
S7:OPPRESEND=1
S8:MIN_FREQ=433050
S9:MAX_FREQ=434790
S10:NUM_CHANNELS=10
S11:DUTY_CYCLE=100
S12:LBT_RSSI=0
S13:MANCHESTER=0
S14:RTSCTS=0
S15:MAX_WINDOW=131
ATI6
ATI6
silence_period: 404
tx_window_width: 6978
max_data_packet_length: 118
ATI7
ATI7
L/R RSSI: 54/0  L/R noise: 66/0 pkts: 0  txe=0 rxe=0 stx=0 srx=0 ecc=0/0 temp=-276 dco=0
ATO

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

La spiegazione dei parametri ripresa da qui e' la seguente

  • FORMAT - this is for EEPROM format version. Don’t change it
  • SERIAL_SPEED - this is the serial speed in ‘one byte form’ (see below)
  • AIR_SPEED - this is the air data rate in ‘one byte form’
  • NETID - this is the network ID. It must be the same for both your radios
  • TXPOWER - this is the transmit power in dBm. The maximum is 20dBm
  • ECC - this enables/disables the golay error correcting code
  • MAVLINK - this controls MAVLink framing and reporting. 0=no mavlink framing, 1=frame mavlink, 2=low latency mavlink
  • MIN_FREQ - minimum frequency in kHz
  • MAX_FREQ - maximum frequency in kHz
  • NUM_CHANNELS - number of frequency hopping channels
  • DUTY_CYCLE - the percentage of time to allow transmit
  • LBT_RSSI - Listen Before Talk threshold (see docs below)
  • MAX_WINDOW - max transmit window in msecs, 131 is the default, 33 recommended for low latency (but lower bandwidth)
i codici sulla potenza di trasmissione sono

Power (dBm)Power (milliWatts)
11.3
21.6
53.2
86.3
1112.5
1425
1750
20100


quindi il modulo radio utilizza effettivamente un firmware SiK e si chiama HM-TRP.
La velocita' di download di default e' 57600

A questo punto ho provato a collegare il secondo modulo via USB ma nessuna risposta (led spenti).. Dronetek non mi ha spiegato il motivo di tale configurazione (ad occhio il modulo da collegare non via Usb e' quello che si monta sul drone mentre l'altro, via Usb, e' da connettere al PC/Tablet di controllo del drone)

Ho provato allora ad alimentare il modulo tramite una Arduino ed il connettore DF13



Visto che il modulo si e' acceso ho caricato un semplice sketch di SoftwareSerial sulla Arduino per mandare una stringa ed ho messo in ascolto con Minicom (57600 8N1) il secondo modulo sulla Linux Box collegato via serial

--------------------------------------------
#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()
{
  mySerial.begin(57600);
}

void loop() // run over and over
{
  mySerial.println("Prova trasmissione");
}
--------------------------------------------

Si e' quindi acceso il led rosso se entrambi i dispositivi radio ed sono riuscito a vedere il flusso dati

Per configurare i moduli radio (per esempio per cambiare il baud rate oppure la potenza di trasmissione) si puo' utilizzare il software Mission Planner

Si seleziona la porta seriale, si seleziona Inital Setup, Sik Radio, Local., Carica Settaggi.





Una soluzione alternativa e' APM Planner

In una prova con potenza di trasmissione 11 corrispondente a 12 mW si riesce a ricevere il segnale fino ad almeno 150 m. In Italia il massimo valore di potenza ammesso per la trasmissione radio e' di 100 mW ma il trasmettitore puo' arrivare a 500 mW

lunedì 27 giugno 2016

Accedere da Arduino a Yocto su Intel Edison

Un altro sistema (dopo quello via seriale visto qui) per scambiare dati tra il lato Arduino ed il lato Edison e' quello di sfruttare il fatto che Arduino IDE espone il file system.

tramite il comando system e' possibile lanciare dei comandi sulla shell di Yocto e potendo creare file. Nel banale esempio sottostante si appende il valore 1 al file /home/root/luca.txt...forgiando la stringa in modo adeguato si possono per esempio salvare i valori letti  da un sensore o le stringhe GPS

mini sketch Arduino
--------------------------
void loop()
{
system("echo '1' >> /home/root/luca.txt");
delay(1000);
}
--------------------------

E' possibile oltre che a scrivere anche leggere i file sul parte Yocto da sketch Arduino

Scambio seriale tra Arduino Expansion Board e Intel Edison

Scambiare dati tra il lato Arduino (via Expansion Board) ed Intel Edison non e' immediato perche' in Yocto non sono predisposte porte seriali.(ripreso da questo link)

Per aggirare il problema si deve installare prima socat, con le istruzioni sotto indicate
----------------------------
wget http://www.dest-unreach.org/socat/download/socat-2.0.0-b8.tar.gz 
tar xvf socat-2.0.0-b8.tar.gz 
cd socat-2.0.0-b8 
configure 
make 
make install
----------------------------
Una volta installato il programma si creano delle interfacce seriali virtuali

nohup socat pty,link=$HOME/tty0,raw,echo=0 pty,link=$HOME/tty1,raw,echo=0 &

(viene generato un messaggio ma non si tratta di un errore)
Si vedra' che in /home/root/ sono comparsi di due dispositivi tty0 e tty1

Adesso si puo' caricare su Arduino IDE lo sketch sottostante che di fatto usa una seriale virtuale che punta su /home/root/tty0
----------------------------------------------
RingBuffer rx_buffer_S1;
TTYUARTClass mySerial(&rx_buffer_S1, 3, false);

void setup() {
  mySerial.init_tty("/home/root/tty0");
  mySerial.begin(115200);
}

void loop()
{
  mySerial.write("luca");
  delay(500);
}
---------------------------------------------

Per leggere i dati inviati dal lato Arduino (in questo caso una semplice stringa) e' sufficiente puntare minicom (o altro terminale seriale) su /home/root/tty1 con i parametri 115200 8N1

Intel Edison e Xbee

Connettere una Intel Edison ha una rete Xbee e' piuttosto semplice ed automatico
Dopo aver montato uno shield Arduino Xbee con il relativo modulo radio (gia' programmato) si invieranno e riceveranno i dati sulla connessione seriale /dev/ttyMFD1 (che di fatto cortocircuita i pin Rx/Tx 0 ed 1 di Arduino verso Yocto)



giovedì 23 giugno 2016

Programmare Arduino Expansion Board su Intel Edison

Edison Intel non e' una vera Arduino (nel senso che non monta un microcontrollore ATMEGA328) ma ha comunque un buon grado di compatibilita' anche per gli shield Arduino

Per la programmazione dei pin Arduino si puo' utilizzare Python o C++ mediante la libreria MRAA ed UPM

Blink led con Python
-----
 import mraa
 led = mraa.Gpio(13)
 led.dir(mraa.DIR_OUT)
led.write(1)
led.write(0)
exit()

-----

Lettura della porta analogica 0
-----
import mraa 
porta = mraa.Aio(0)
while 1: 
     valore = float(pot.read())
     print valore
-----

usando la libreria UPM sono gia' disponibili molti esempi su diversi sensori a questo indirizzo

Si puo' utilizzare anche Arduino IDE per controllare i pin. In questo caso si deve in modo preliminare configurare la scheda andando in Strumenti/Scheda/Gestore Schede installando Intel i686 Boards (Edison)

Utilizzando Arduino IDE l'upload degli sketch avviene in modo tradizionale. L'unica differenza fondamentale e' che gli sketch vengono salvati in /tmp per cui al successivo riavvio non sara'  piu ' disponibile. Per ovviare a questo problema si deve procedere come

cp /tmp/nome_sketch /sketch/sketch.elf
chmod 755 /sketch.sketch.elf


L'Expansion Board puo' essere programmata anche tramite C/C++. Per fare cio' si usa Intel System Studio IoT Edition (sostanzialmente una versione modificata di Eclipse)


Per lanciare il programma si usa ./iss-iot-launcher


Di fatto si tratta di programmazione remota perche' il file viene compilato sul PC e poi inviato ed eseguito sulla Edison via SSH (WiFi, Ethernet-over-Usb). Si deve quindi, prima di procedere, creare una connessione con Edison

I file compilati vengono copiati sulla /tmp di Edison. Per questo motivo non saranno disponibili dopo un riavvio a meno di non  copiare il file eseguibile in un'altra posizione

Blink in C++
----------------------------------------------
/*
 * Author: Jessica Gomez <jessica.gomez.hernandez@intel.com>
 * Copyright (c) 2015 - 2016 Intel Corporation.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "mraa.h"

#include <stdio.h>
#include <unistd.h>

/*
 * On board LED blink C example
 *
 * Demonstrate how to blink the on board LED, writing a digital value to an
 * output pin using the MRAA library.
 * No external hardware is needed.
 *
 * - digital out: on board LED
 *
 * Additional linker flags: none
 */

int main()
{
// select onboard LED pin based on the platform type
// create a GPIO object from MRAA using it
mraa_platform_t platform = mraa_get_platform_type();
mraa_gpio_context d_pin = NULL;
switch (platform) {
case MRAA_INTEL_GALILEO_GEN1:
d_pin = mraa_gpio_init_raw(3);
break;
case MRAA_INTEL_GALILEO_GEN2:
d_pin = mraa_gpio_init(13);
break ;
case MRAA_INTEL_EDISON_FAB_C:
d_pin = mraa_gpio_init(13);
break;
default:
fprintf(stderr, "Unsupported platform, exiting");
return MRAA_ERROR_INVALID_PLATFORM;
}
if (d_pin == NULL) {
fprintf(stderr, "MRAA couldn't initialize GPIO, exiting");
return MRAA_ERROR_UNSPECIFIED;
}

// set the pin as output
if (mraa_gpio_dir(d_pin, MRAA_GPIO_OUT) != MRAA_SUCCESS) {
fprintf(stderr, "Can't set digital pin as output, exiting");
return MRAA_ERROR_UNSPECIFIED;
};

// loop forever toggling the on board LED every second
for (;;) {
mraa_gpio_write(d_pin, 0);
sleep(1);
mraa_gpio_write(d_pin, 1);
sleep(1);
}

return MRAA_SUCCESS;
}
----------------------------------------------

Node.JS

nonostante sia possibile programmare Edison con Node:JS non sono riuscito a far funzionare XKD Iot su Centos. Si puo' comunque usare Node. direttamente da shell senza usare la programmazione remota

Blink led con Node.JS
-----
var mraa = require('mraa'); 
console.log('MRAA Version: ' + mraa.getVersion());
var myOnboardLed = new mraa.Gpio(13); 
myOnboardLed.dir(mraa.DIR_OUT); 
-----

Lettura della porta analogica 0
-----
var mraa = require('mraa'); 
console.log('MRAA Version: ' + mraa.getVersion()); 
var analogPin0 = new mraa.Aio(0); 
var analogValue = analogPin0.read(); 
console.log(analogValue);
-----

Ethernet-over-USB per Intel Edison su Linux

La Intel Edison oltre alla connessione della consolle via Seriale e via WiFi dispone anche della possibilita' di stabilitre una connessione TCP anche via cavo Usb con il protocollo Ethernet-over-USB (su Windows prende il nome del protocollo proprietario RNDIS)

Se si collega Edison con la porta USB-OTG e si digita sul Pc il comando
ip a

si ottiene un nuovo dispositivo che nel mio caso specifico e' enp0s26u1u1uc2.
Intel Edison ha un Ip statico sulla connessione USB (192.168.2.15). Per ottenere una connessione SSH via USB e' sufficiente impostare l'ip della connessione lato PC con

ifconfig enp0s26u1u1uc2 192.168.2.2

a questo punto si puo' lanciare la sessione ssh con
ssh root@192.168.2.15

questa configurazione puo' essere utile nel caso si voglia programmare il dispositivo utilizzando iss-iot-linux (l'ambiente di sviluppo Linux/Eclipse per Edison) senza la necessita' di attivare un access point

Flash firmware su Intel Edison

E' passato un paio di settimane che gioco con Intel Edison e sono gia' nella necessita' di flashare il firmware (cercando di installare un dongle USB-Ethernet ho incasinato la connessione WiFi)




Per ripristinare il sistema operativo di Edison si parte scaricando il file
iot-devkit-prof-dev-image-edison-2010606.zip

p
Prima di procedere oltre si deve installare il programma dfu-util.
La sequenza e' la seguente

git clone git://git.code.sf.net/p/dfu-util/dfu-util
cd dfu-util
./autogen.sh
./configure
make
make install

a questo punto si puo' lanciare il file ./flash_all.sh

Si vedono scorrere i seguenti messaggi (il tempo e' di circa 5 minuti)
------------------------------------------------------
[root@localhost immagine]# ./flashall.sh
Using U-Boot target: edison-blankcdc
Now waiting for dfu device 8087:0a99
Please plug and reboot the board
Flashing IFWI
Download    [=========================] 100%      4194304 bytes
Download    [=========================] 100%      4194304 bytes
Flashing U-Boot
Download    [=========================] 100%       237568 bytes
Flashing U-Boot Environment
Download    [=========================] 100%        65536 bytes
Flashing U-Boot Environment Backup
Download    [=========================] 100%        65536 bytes
Rebooting to apply partition changes
Now waiting for dfu device 8087:0a99
Flashing boot partition (kernel)
Download    [=========================] 100%      6144000 bytes
Flashing rootfs, (it can take up to 5 minutes... Please be patient)
Download    [=========================] 100%   1373149184 bytes
Rebooting
U-boot & Kernel System Flash Success...
Your board needs to reboot to complete the flashing procedure, please do not unplug it for 2 minutes.


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

Al reboot si collega il cavo USB alla porta seriale per ottenere una consolle

screen /dev/ttyUSB1 115200

(per terminare una connessione screen CTRL+A CTRL+D)

di default l'utente root non ha password. Al login basta digitare root e si entra in shell
Per ottenere la connessione SSH si deve prima configurare la password del dispositivo con

configure-edison --password

a questo punto si configura la connessione WiFi con
configure-edison --wifi

il dispositivo, oltre all'Ip, puo' essere anche richiamato come edison.local

per terminare e' sufficiente installare gli aggiornamenti di sistema

opkg update
opkg upgrade

Facendo cio' si genera il seguente errore
Failed to download http://iotdk.intel.com/repos/3.5/intelgalactic/opkg/i586//Packages

l'errore e' generato dal fatto che la directory non esiste: Guardando al sistema precedente (il 3.0) nella directory http://iotdk.intel.com/repos/3.0/intelgalactic/opkg/i586/ sono contenuti gli aggiornamenti per la libreria mraa e upm ma questi nel sistema 3.5 sono contenuti in http://iotdk.intel.com/repos/3.5/iotdk/edison/core2-32/

Per questo motivo credo si tratti solo di un refuso nel file dei repository nel passaggio da 3.0 a 3.5



mercoledì 22 giugno 2016

Windows 10 Update: Connettivita' limitata su WiFi

Dopo un corposo aggiornamento di Windows 10 (tanto corposo che ho lasciato il calcolatore acceso di notte in attesa del termine) mi sono trovato il giorno successivo con il messaggio di Connettivita' Limitata sulla rete WiFi (il che tradotto vuol dire che il Pc si era collegato all'Access Point ma non era in grado di raggiungere Internet)



L'aggiornamento era stato via WiFi quindi la rete senza fili funzionava. Usando un Surface (hardware Microsoft) mi sono dovuto attrezzare con un dongle Usb-Ethernet per riguadagnare Internet e vedere se c'erano ulteriori aggiornamenti in attesa di installazione. Dopo un altro corposo aggiornamento la situazione e' rimasta identica...niente connessione via WiFi.

Questo problema era segnalato da altri utenti ma erano situazioni legate a upgrade da Windows 8 a Windows 10...quindi non il mio caso

La soluzione e' stata quella di ripristinare il driver della scheda di rete senza fili.
Certo che se Windows non funziona nemmeno su hardware Microsoft......

martedì 21 giugno 2016

Webcam su Intel Edison

Il primo passo per utilizzare una webcam su Intel Edison e' quella di spostare il selettore SW2 dalla parte della porta USB standard. In questo  modo si disattiva la porta microUsb OTG e si attiva la porta USB standard

Dal punto di vista dei moduli del kernel la mia Logitech C310 e' stata vista senza troppi problemi e montata in /dev/video0



Partendo dai repositori dai repository di AlexT (vedi qui) e' banale installare motion 

motion in azione

Piu' problematico invece far funzionare mjpeg-streamer. Scaricando via opkg da AlexT il pacchetto risulta andare in segmentation fault. Ho provato a ricompilare quindi dai sorgenti (l'ultima versione disponibile e' la r63) risolvendo le dipendenze e quindi installando anche v4l-utils.


In fase di compilazione pero' mjpeg-streamer si e' rifiutato per un errore piuttosto criptico su un plugin, penso dovuto al fatto che io ho installato v4l2 mentre mpjeg-streamer usa v4l.

Frugando su github ho trovato v4l2_mjpeg_streamer, una versione alleggerita di mjpeg_streamer basata su v4l2 che permette una trasmissione a 5 fotogrammi per secondo alla massima risoluzione della camera (al contrario di mjpeg_streamer la porta TCP e' la 8081)








venerdì 17 giugno 2016

Accuratezza di sensore ultrasonico SR-04

Per un lavoro di laboratorio e' necessario misurare la distanza di un campione senza toccarlo con un comparatore. Per questo motivo e' stata provato ad usare il sensore ultrasonico SR-04 come misuratore di distanza
La precisione richiesta era di 1 mm mentre le schede tecniche che si trovano su Internet parlano di un valore minimo di risoluzione di 3 mm..in ogni caso e' stata fatta una prova



Il bersaglio era costituito da una lamina di alluminio e il sensore e' stato collegato ad una Arduino Uno. Purtroppo non e' stato possibile mettere su un sistema che permetteva la perfetta ortogonalita' della sensore emittente con la superficie riflettente (questo e' un limite della presente misura)

Visto che il sensore non misura direttamente la distanza ma il tempo di volo di un impulso (la distanza si calcola a ritroso conoscendo la velocita' del suono nell'aria) e visto che la velocita' del suono nell'aria e' funzione della temperatura (e non della pressione) secondo un legge del tipo

velocita'_suono=331.4+0.62*temperatura (m/sec)

ho misurato il tempo di volo tramite la libreria NewPing spostando il bersaglio posto a distanza variabile intorno ai 20 cm, Per ogni posizione sono state effettuate 500 acquisizione che poi sono state mediate mediante la libreria Statistics di Arduino

(di seguito si usa il simbolo ms per indicare i microsecondi)

Lo sketch impiegato e' stato il seguente
------------------------------------------------
#include <NewPing.h>
#include "Statistic.h" 

#define TRIGGER_PIN  12  // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN     11  // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 50 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); // NewPing setup of pins and maximum distance.

Statistic myStats; 

void setup() {
  Serial.begin(115200); 
  myStats.clear();
}

void loop() {
  delay(50);                     
  myStats.add(sonar.ping());
  if (myStats.count() == 500)
        {
        Serial.print("  Average: ");
        Serial.print(myStats.average(), 2);
        Serial.print("  Std deviation: ");
        Serial.print(myStats.pop_stdev(), 2);
        Serial.println();
        }
}
------------------------------------------------

I dati, di una prova sicuramente parziale ma comunque indicativa della fattibilita', e' riportata nella figura successiva
Attenzione: si parla di tempi di andata e ritorno dell'impulso.



Molto indicativamente si ha che un centimetro di distanza corrisponde, nelle condizioni di misura, a circa 60 ms quindi se si vuole cerca di misurare una variazione di 1mm si deve riuscire a separare almeno 6 ms. Considerando che l'errore medio di deviazione standard e' di circa 3 ms usare l'SR-04 per misurare con la precisione del millimetro e' decisamente al limite delle proprie possibilita' (si comincia a ragionare se la risoluzione e' un decimo della grandezza che si vuole misurare)


secondo i dati teorici con  temperatura di 20°C la velocita' del suono in aria e' di 0.0343 cm/ms (343 m/s) il tempo di volo e' di 6 ms per mm (andata e ritorno 2 mm) il che ritorna abbastanza bene con quanto misurato

mercoledì 15 giugno 2016

Amplificatore invertente AC con LM358

Ho fatto qualche esperimento con LM358 in passato (con risultati deludenti..per colpa mia).
Frugando su Internet ho trovato questo link dove viene indicato l'utilizzo di LM358 con un configurazione che non avevo mai usato

Il testo di riferimento citato nel testo del post non e' piu' disponibile ma sono riuscito a trovare una copia di Ops amps for Everyone a questo link


Con la configurazione sotto riportata il fattore di amplificazione e' data dalla tensione di ingresso per il rapporto tra Rf ed Rg piu' la tensione di alimentazione divisa per due (le due resistenze R di uguale entita' funzionano come un divisore di tensione)




Non avendo un microfono audio o un generato di segnale  mi sono adattato a vedere l'amplificazione data da un piezoelettrico. In condizioni non amplificate il segnale massimo registrato e' stato di circa 5/6 unita' sull'ingresso analogico



passandolo per l'amplificatore si vede chiaramente che viene amplificato anche il rumore e l'intesita' del segnale e' passata a qualche centinaio di unita' (ho perso i dettagli delle resistenze che ho usato). Si vede anche che il segnale e' stato spostato al centro dell'asse delle Y (valore di circa 500 con una fondo scale di 1024 a bit 10)








martedì 14 giugno 2016

Crearsi una scatola per GPS Arduino

Quando un progetto di Arduino deve diventare operativo e' necessario costruirsi una scatola che alloggi l'elettronica e gli accessori. In questo post descrivo come ho creato la scatola per il mio GPS Arduino Ublox

Il primo passo fondamentale e' munirsi di distanziali (spacer) per bloccare la scheda elettrica alla scatola


Ne esistono sia in metallo (credo sia ottone) che in nylon. Io ho usato quelli in plastica. Il passo della vite e' da 3 per cui si puo' comprare anche minuteria metallica se la lunghezza delle viti del set non e' idonea

Come scatola ho preso una Gewiss GW 44 007 classificata come IP 55 (ovvero Protezione totale alla penetrazione di corpi solidi.Protezione contro la penetrazione di liquidi da gocce, vapori, spruzzi e getti d'acqua in qualsiasi direzione.)

L'utilizzo di questa scatola ha il vantaggio di avere dei tappi removibili che permettono il passaggio di un cavo USB di programmazione per poi richiudere il tutto senza necessariamente dovere rimuovere il coperchio e le sue viti

Modalita' di programmazione


Il progetto prevede di montare sul coperchio l'antenna GPS (all'interno) ed il pannello solare (all'esterno)


Montati i distanziali sull'antenna si devono fare i fori sul coperchio...ma come farli precisi?? Un piccolo trucco. Si macchia il distanziale con un pennarello, essendo di nylon l'inchiostro non penetra e basta appoggiare il tutto sul coperchio per ottenere l'impronta di dove forare.La punta del trapano da usare e' quella da 3

Il risultato finale e' questo



Per montare il pannello solare si deve praticare un foro per passare i fili all'interno della scatola e dei distanziali per sostenere il pannello. Visto che il pannello solare era privo di prefori ho usato dei gommini di plastica fermati da una vite a ferro (fare prima un preforo prima di avvitare la vite perche' la plastica e' molto scivolosa e ci vuole un foro guida)


Per fissare il pannello al coperchio e' sufficiente usare una abbondante dose di silicone


Si passa all'interno della scatola. Su Arduino Uno sono stati montati gli spacer (in questo caso hanno trovato posto solo tre spacer su quattro fori perche' il dado di serraggio dello spacer e' troppo grande...non un gran male perche' comunque sul fondo della scatola ci sono dei bordi rialzati che si possono utilizzare come appoggio)
Per trovare il punto giusto di realizzazione dei fori si usa lo stesso metodo precedente



La batteria invece e' stata bloccata con del velcro.

A questo punto si devono mettere insieme i  pezzi (scatola e coperchio). Qui l'unica attenzione e' quella di allungare tutti i cavi in modo da rendere agevole. E' anche comodo scegliere un lato di apertura della scatola e di marcarla sulla scatola in modo da aprire sempre dallo stesso verso (questo per non aprire per sbaglio un un lato per cui i cavi vanno in tensione)




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
}