Hacer compatible C++ códigos de C#.

Iniciado por Meta, 23 Febrero 2015, 23:17 PM

0 Miembros y 2 Visitantes están viendo este tema.

Meta

Hola:
Tengo este código dentro de un button para enviar tramas de bytes hecho con C#.

Código (cpp) [Seleccionar]
// Enviar tramas de bytes.

            byte[] miBuffer = new byte[9]; // Led_13_ON son 9 byte máximo.
            miBuffer[0] = 0x4C; // ASCII letra "L".
            miBuffer[1] = 0x65; // ASCII letra "e".
            miBuffer[2] = 0x64; // ASCII letra "d".
            miBuffer[3] = 0x5F; // ASCII letra "_".
            miBuffer[4] = 0x31; // ASCII letra "1".
            miBuffer[5] = 0x33; // ASCII letra "3".
            miBuffer[6] = 0x5F; // ASCII letra "_".
            miBuffer[7] = 0x4F; // ASCII letra "O".
            miBuffer[8] = 0x4E; // ASCII letra "N".
            serialPort1.Write(miBuffer, 0, miBuffer.Length); // Envia las tramas de bytes.


Da igual cuantras tramas hay que enviar, en C++ se hace así com indica abajo enviando la letra t.

Código (cpp) [Seleccionar]
cli::array<unsigned char> ^uno = gcnew cli::array<unsigned char> (1);
uno[0] = 0x74; // ASCII letra "t".
serialPort1->Write(uno, 0, 1);


Quiero hacer una cadena escrito más cómodamente como el ejemplo en C# hecho abajo.

Código (cpp) [Seleccionar]
            byte[] mBuffer = Encoding.ASCII.GetBytes("Led_8_ON");
            serialPort1.Write(mBuffer, 0, mBuffer.Length);


¿Cómo se hace en Visual C++ 2010?

Gracias.
Tutoriales Electrónica y PIC: http://electronica-pic.blogspot.com/

Meta

Hola:

Intenté ahcerlo así:
Código (cpp) [Seleccionar]
cli::array<unsigned char> ^mBuffer = Encoding::ASCII->GetBytes("Led_8_ON");
serialPort1->Write(mBuffer, 0, mBuffer->Length);


Me da errores.
Tutoriales Electrónica y PIC: http://electronica-pic.blogspot.com/

Eleкtro

Cita de: Meta en 24 Febrero 2015, 01:51 AMMe da errores.
¿Vas a mostrar y comentar el mensaje de error de compilación, o debemos adivinarlo?.  :¬¬




Practicamente no manejo nada en absoluto VC++, pero dispongo de una herramienta traductora privada que es bastante fiable, prueba utilizando esta sintaxis:
Código (cpp) [Seleccionar]
array<Byte> ^mBuffer = Encoding::ASCII->GetBytes("Led_8_ON");
serialPort1::Write(mBuffer, 0, mBuffer->Length);


Saludos!








Meta

Hola:

Tienes razón. Aquí están los errores que he me salió.
Citar------ Operación Generar iniciada: proyecto: InterDuinoCPP, configuración: Debug Win32 ------
  InterDuinoCPP.cpp
c:\users\meta\documents\visual studio 2010\projects\interduinocpp\interduinocpp\Form1.h(195): error C2653: 'Enconding' : no es un nombre de clase o espacio de nombres
c:\users\meta\documents\visual studio 2010\projects\interduinocpp\interduinocpp\Form1.h(195): error C2065: 'ASCII' : identificador no declarado
c:\users\meta\documents\visual studio 2010\projects\interduinocpp\interduinocpp\Form1.h(195): error C2227: el operando izquierdo de '->GetBytes' debe señalar al tipo class/struct/union/generic
          el tipo es ''unknown-type''
c:\users\meta\documents\visual studio 2010\projects\interduinocpp\interduinocpp\Form1.h(196): error C2664: 'void System::IO::Ports::SerialPort::Write(System::String ^)' : no se puede convertir el parámetro 1 de 'int' a 'System::String ^'
          No hay un operador de conversión definida por el usuario disponible, o
          No existe una conversión estándar del formulario al que se le aplica la conversión boxing del tipo aritmético al tipo de destino
========== Generar: 0 correctos, 1 incorrectos, 0 actualizados, 0 omitidos ==========

En cuanto a tu código, da más errores y los mismos que estos del ASCII, Encoding.

Saludos y gracias por el intento.
Tutoriales Electrónica y PIC: http://electronica-pic.blogspot.com/

Eleкtro

#4
El primer, el segundo, y el tercer error, suceden por que no se encuentra el namespace (como te está indicando). Importa el namespace donde se haya el miembro "Encoding" (System.Text.Encoding)...

Código (cpp) [Seleccionar]
using namespace System;
using namespace System::Text;


El último error, se explica por si mismo también, el método SerialPort.Write no acepta el datatype Int como primer parámetro, sino un array de Byte, de Char, o un String. Cómo explica la MSDN:
https://msdn.microsoft.com/en-us/library/ms143551%28v=vs.110%29.aspx?cs-save-lang=1&cs-lang=cpp#code-snippet-1
El método por defecto acepta un String, y al no poder convertir un Int a String... pum.

Me imagino que al corregir los tres primeros errores de compilación referente al namespace y así poder asignar el array de bytes, se auto-corregirá el error de parametización del método serialport1.write() al mismo tiempo, puesto que estás intentando utilizar el overload que acepta un array de bytes.


PD: ¿Ves que pronto se solucionan más o menos las cosas cuando uno especifica la información necesaria al formular una pregunta de programación sobre un error?, que no eres nuevo en el foro, deberías saber ya lo molesto que resulta eso.

Saludos!








Meta

#5
Hola Señor:

Aquí en español.
https://msdn.microsoft.com/es-es/library/ms143551%28v=vs.110%29.aspx?cs-save-lang=1&cs-lang=cpp&f=255&MSPPError=-2147217396#code-snippet-1

Aquí sobre Encoding y ASCCIEncoding, he puesto estos dos uses y nada.
https://msdn.microsoft.com/es-es/library/system.text.asciiencoding%28v=vs.110%29.aspx

A pesar que parece que es así:
[HIGHLIGHT="C++"]    cli::array<unsigned char> ^mBuffer = Encoding::ASCII->GetBytes("Led_8_ON");
    serialPort1->Write(mBuffer, 0, mBuffer->Length);[/HIGHLIGHT]

No lo es, todavía con los mismos errores que salen.

Lo que he hecho.


Paso 2.


Paso 3.



Paso 4.


Haces doble clic enun button y poner el código.

Este código C# quiero traducirlo a Visual C++ 2010.
private void button_b_Click(object sender, EventArgs e)
{
    byte[] mBuffer = Encoding.ASCII.GetBytes("Hello World");
    serialPort1.Write(mBuffer, 0, mBuffer.Length);
}


Saludos.
Tutoriales Electrónica y PIC: http://electronica-pic.blogspot.com/

Meta

#6
Me ha servido enviar tramas, ahora toca recibir.

Código (cpp,52, 59) [Seleccionar]
#pragma once

namespace Project1 {

using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;

using namespace System::IO::Ports; // No olvidar.
using namespace System::Text;

/// <summary>
/// Resumen de MyForm
/// </summary>
public ref class MyForm : public System::Windows::Forms::Form
{
// Utilizaremos un string como buffer de recepción.
String^ Recibidos;

public:
MyForm(void)
{
InitializeComponent();
//
//TODO: agregar código de constructor aquí
//
if (!serialPort1->IsOpen)
{
try
{
serialPort1->Open();
}
catch (Exception^ex)
{
MessageBox::Show(ex->ToString());
}
// Ejecutar la función Recepcion por disparo del Evento 'DataReived'.
serialPort1->DataReceived += gcnew SerialDataReceivedEventHandler(Recepcion);
}
}

// Al recibir datos.
private: void Recepcion(Object^ sender, SerialDataReceivedEventArgs^ e)
{
// Acumula los caracteres recibidos a nuestro 'buffer' (sting).
Recibidos += serialPort1->ReadExisting();

// Invocar o llamar al proceso de tramas.
Invoke(gcnew EventHandler(Actualizar));
}

// Procesar los datos recibidos en el buffer y estraer tramas completas.
private: void Actualizar(Object^ sender, EventArgs^ e)
{
// Asignar el valor de la trama al richTextBox.
richTextBox_Mensajes->Text = Recibidos;

// Selecciona la posición final para leer los mensajes entrantes.
richTextBox_Mensajes->SelectionStart = richTextBox_Mensajes->Text->Length;

// Mantiene el scroll en la entrada de cada mensaje.
richTextBox_Mensajes->ScrollToCaret();
}

protected:
/// <summary>
/// Limpiar los recursos que se estén utilizando.
/// </summary>
~MyForm()
{
if (components)
{
delete components;
}
}
private: System::Windows::Forms::Button^  button_Led_8_ON;
protected:
private: System::Windows::Forms::Button^  button_Led_8_OFF;
private: System::IO::Ports::SerialPort^  serialPort1;
private: System::Windows::Forms::RichTextBox^  richTextBox_Mensajes;
private: System::ComponentModel::IContainer^  components;

protected:

private:
/// <summary>
/// Variable del diseñador requerida.
/// </summary>


#pragma region Windows Form Designer generated code
/// <summary>
/// Método necesario para admitir el Diseñador. No se puede modificar
/// el contenido del método con el editor de código.
/// </summary>
void InitializeComponent(void)
{
this->components = (gcnew System::ComponentModel::Container());
this->button_Led_8_ON = (gcnew System::Windows::Forms::Button());
this->button_Led_8_OFF = (gcnew System::Windows::Forms::Button());
this->serialPort1 = (gcnew System::IO::Ports::SerialPort(this->components));
this->richTextBox_Mensajes = (gcnew System::Windows::Forms::RichTextBox());
this->SuspendLayout();
//
// button_Led_8_ON
//
this->button_Led_8_ON->Location = System::Drawing::Point(33, 39);
this->button_Led_8_ON->Name = L"button_Led_8_ON";
this->button_Led_8_ON->Size = System::Drawing::Size(75, 23);
this->button_Led_8_ON->TabIndex = 0;
this->button_Led_8_ON->Text = L"ON";
this->button_Led_8_ON->UseVisualStyleBackColor = true;
this->button_Led_8_ON->Click += gcnew System::EventHandler(this, &MyForm::button_Led_8_ON_Click);
//
// button_Led_8_OFF
//
this->button_Led_8_OFF->Location = System::Drawing::Point(33, 81);
this->button_Led_8_OFF->Name = L"button_Led_8_OFF";
this->button_Led_8_OFF->Size = System::Drawing::Size(75, 23);
this->button_Led_8_OFF->TabIndex = 1;
this->button_Led_8_OFF->Text = L"OFF";
this->button_Led_8_OFF->UseVisualStyleBackColor = true;
this->button_Led_8_OFF->Click += gcnew System::EventHandler(this, &MyForm::button_Led_8_OFF_Click);
//
// serialPort1
//
this->serialPort1->BaudRate = 115200;
this->serialPort1->PortName = L"COM4";
//
// richTextBox_Mensajes
//
this->richTextBox_Mensajes->Dock = System::Windows::Forms::DockStyle::Bottom;
this->richTextBox_Mensajes->Location = System::Drawing::Point(0, 137);
this->richTextBox_Mensajes->Name = L"richTextBox_Mensajes";
this->richTextBox_Mensajes->Size = System::Drawing::Size(284, 125);
this->richTextBox_Mensajes->TabIndex = 2;
this->richTextBox_Mensajes->Text = L"";
//
// MyForm
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(284, 262);
this->Controls->Add(this->richTextBox_Mensajes);
this->Controls->Add(this->button_Led_8_OFF);
this->Controls->Add(this->button_Led_8_ON);
this->Name = L"MyForm";
this->StartPosition = System::Windows::Forms::FormStartPosition::CenterScreen;
this->Text = L"MyForm";
this->ResumeLayout(false);

}
#pragma endregion
private: System::Void button_Led_8_ON_Click(System::Object^  sender, System::EventArgs^  e) {
cli::array<unsigned char> ^miBuffer = gcnew cli::array<unsigned char>(9);
miBuffer[0] = 0x4C; // ASCII letra "L".
miBuffer[1] = 0x65; // ASCII letra "e".
miBuffer[2] = 0x64; // ASCII letra "d".
miBuffer[3] = 0x5F; // ASCII letra "_".
miBuffer[4] = 0x31; // ASCII letra "1".
miBuffer[5] = 0x33; // ASCII letra "3".
miBuffer[6] = 0x5F; // ASCII letra "_".
miBuffer[7] = 0x4F; // ASCII letra "O".
miBuffer[8] = 0x4E; // ASCII letra "N".
serialPort1->Write(miBuffer, 0, miBuffer->Length);
}
private: System::Void button_Led_8_OFF_Click(System::Object^  sender, System::EventArgs^  e) {
array<Byte> ^miBuffer = Encoding::ASCII->GetBytes("Led_13_OFF");
serialPort1->Write(miBuffer, 0, miBuffer->Length);
}
};
}


Me dan estos errores y creo que es por mala traducción.
CitarError   2   error C3350: 'System::IO::Ports::SerialDataReceivedEventHandler' : un constructor de delegado espera 2 argumentos   c:\users\meta\documents\visual studio 2013\projects\project1\project1\MyForm.h   41   1   Project1

Otro error.
CitarError   3   error C3867: 'Project1::MyForm::Actualizar': falta la lista de argumentos de la llamada a la función; utilice '&Project1::MyForm::Actualizar' para crear un puntero al miembro   c:\users\meta\documents\visual studio 2013\projects\project1\project1\MyForm.h   52   1   Project1
Tutoriales Electrónica y PIC: http://electronica-pic.blogspot.com/

Meta

Ya encontré la solución y funciona.

Se los dejo aquí por si alguien le hace falta o le pueda interesar.

Código (cpp) [Seleccionar]
#pragma once

namespace InterDuinoCPP {

using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;

using namespace System::IO::Ports; // No olvidar.
using namespace System::Text;

/// <summary>
/// Resumen de Form_Principal
/// </summary>
public ref class Form_Principal : public System::Windows::Forms::Form
{
public:
Form_Principal(void)
{
InitializeComponent();
//
//TODO: agregar código de constructor aquí
//

// Abrir puerto miestras se ejecuta la aplicación.
if (!serialPort1->IsOpen)
{
try
{
serialPort1->Open();
}
catch (Exception^ex)
{
MessageBox::Show(ex->ToString());
}
}
}

protected:
/// <summary>
/// Limpiar los recursos que se estén utilizando.
/// </summary>
~Form_Principal()
{
if (components)
{
delete components;
}
}
private: System::Windows::Forms::Button^  button_Led_8_ON;
private: System::Windows::Forms::Button^  button_Led_8_OFF;
protected:

protected:

private: System::Windows::Forms::Label^  label1;
private: System::Windows::Forms::Button^  button_Led_13_ON;
private: System::Windows::Forms::Button^  button_Led_13_OFF;


private: System::Windows::Forms::Label^  label2;
private: System::Windows::Forms::RichTextBox^  richTextBox_Mensajes;

private: System::Windows::Forms::Label^  label3;
private: System::IO::Ports::SerialPort^  serialPort1;
private: System::ComponentModel::IContainer^  components;

private:
/// <summary>
/// Variable del diseñador requerida.
/// </summary>


#pragma region Windows Form Designer generated code
/// <summary>
/// Método necesario para admitir el Diseñador. No se puede modificar
/// el contenido del método con el editor de código.
/// </summary>
void InitializeComponent(void)
{
this->components = (gcnew System::ComponentModel::Container());
this->button_Led_8_ON = (gcnew System::Windows::Forms::Button());
this->button_Led_8_OFF = (gcnew System::Windows::Forms::Button());
this->label1 = (gcnew System::Windows::Forms::Label());
this->button_Led_13_ON = (gcnew System::Windows::Forms::Button());
this->button_Led_13_OFF = (gcnew System::Windows::Forms::Button());
this->label2 = (gcnew System::Windows::Forms::Label());
this->richTextBox_Mensajes = (gcnew System::Windows::Forms::RichTextBox());
this->label3 = (gcnew System::Windows::Forms::Label());
this->serialPort1 = (gcnew System::IO::Ports::SerialPort(this->components));
this->SuspendLayout();
//
// button_Led_8_ON
//
this->button_Led_8_ON->Location = System::Drawing::Point(43, 37);
this->button_Led_8_ON->Name = L"button_Led_8_ON";
this->button_Led_8_ON->Size = System::Drawing::Size(75, 23);
this->button_Led_8_ON->TabIndex = 0;
this->button_Led_8_ON->Text = L"ON";
this->button_Led_8_ON->UseVisualStyleBackColor = true;
this->button_Led_8_ON->Click += gcnew System::EventHandler(this, &Form_Principal::button_Led_8_ON_Click);
//
// button_Led_8_OFF
//
this->button_Led_8_OFF->Location = System::Drawing::Point(43, 77);
this->button_Led_8_OFF->Name = L"button_Led_8_OFF";
this->button_Led_8_OFF->Size = System::Drawing::Size(75, 23);
this->button_Led_8_OFF->TabIndex = 1;
this->button_Led_8_OFF->Text = L"OFF";
this->button_Led_8_OFF->UseVisualStyleBackColor = true;
this->button_Led_8_OFF->Click += gcnew System::EventHandler(this, &Form_Principal::button_Led_8_OFF_Click);
//
// label1
//
this->label1->AutoSize = true;
this->label1->Location = System::Drawing::Point(62, 21);
this->label1->Name = L"label1";
this->label1->Size = System::Drawing::Size(34, 13);
this->label1->TabIndex = 2;
this->label1->Text = L"Led 8";
//
// button_Led_13_ON
//
this->button_Led_13_ON->Location = System::Drawing::Point(166, 37);
this->button_Led_13_ON->Name = L"button_Led_13_ON";
this->button_Led_13_ON->Size = System::Drawing::Size(75, 23);
this->button_Led_13_ON->TabIndex = 3;
this->button_Led_13_ON->Text = L"ON";
this->button_Led_13_ON->UseVisualStyleBackColor = true;
this->button_Led_13_ON->Click += gcnew System::EventHandler(this, &Form_Principal::button_Led_13_ON_Click);
//
// button_Led_13_OFF
//
this->button_Led_13_OFF->Location = System::Drawing::Point(166, 77);
this->button_Led_13_OFF->Name = L"button_Led_13_OFF";
this->button_Led_13_OFF->Size = System::Drawing::Size(75, 23);
this->button_Led_13_OFF->TabIndex = 4;
this->button_Led_13_OFF->Text = L"OFF";
this->button_Led_13_OFF->UseVisualStyleBackColor = true;
this->button_Led_13_OFF->Click += gcnew System::EventHandler(this, &Form_Principal::button_Led_13_OFF_Click);
//
// label2
//
this->label2->AutoSize = true;
this->label2->Location = System::Drawing::Point(186, 21);
this->label2->Name = L"label2";
this->label2->Size = System::Drawing::Size(40, 13);
this->label2->TabIndex = 5;
this->label2->Text = L"Led 13";
//
// richTextBox_Mensajes
//
this->richTextBox_Mensajes->Dock = System::Windows::Forms::DockStyle::Bottom;
this->richTextBox_Mensajes->Location = System::Drawing::Point(0, 129);
this->richTextBox_Mensajes->Name = L"richTextBox_Mensajes";
this->richTextBox_Mensajes->Size = System::Drawing::Size(284, 133);
this->richTextBox_Mensajes->TabIndex = 6;
this->richTextBox_Mensajes->Text = L"";
//
// label3
//
this->label3->AutoSize = true;
this->label3->Location = System::Drawing::Point(12, 113);
this->label3->Name = L"label3";
this->label3->Size = System::Drawing::Size(121, 13);
this->label3->TabIndex = 7;
this->label3->Text = L"Mensaje desde Arduino:";
//
// serialPort1
//
this->serialPort1->BaudRate = 115200;
this->serialPort1->PortName = L"COM4";
this->serialPort1->DataReceived += gcnew System::IO::Ports::SerialDataReceivedEventHandler(this, &Form_Principal::serialPort1_DataReceived);
//
// Form_Principal
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 13);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->ClientSize = System::Drawing::Size(284, 262);
this->Controls->Add(this->label3);
this->Controls->Add(this->richTextBox_Mensajes);
this->Controls->Add(this->label2);
this->Controls->Add(this->button_Led_13_OFF);
this->Controls->Add(this->button_Led_13_ON);
this->Controls->Add(this->label1);
this->Controls->Add(this->button_Led_8_OFF);
this->Controls->Add(this->button_Led_8_ON);
this->Name = L"Form_Principal";
this->StartPosition = System::Windows::Forms::FormStartPosition::CenterScreen;
this->Text = L"Mini Interfaz C++";
this->ResumeLayout(false);
this->PerformLayout();

}
#pragma endregion
private: System::Void button_Led_8_ON_Click(System::Object^  sender, System::EventArgs^  e) {
array<Byte> ^miBuffer = Encoding::ASCII->GetBytes("Led_8_ON");
serialPort1->Write(miBuffer, 0, miBuffer->Length);
}
private: System::Void button_Led_8_OFF_Click(System::Object^  sender, System::EventArgs^  e) {
array<Byte> ^miBuffer = Encoding::ASCII->GetBytes("Led_8_OFF");
serialPort1->Write(miBuffer, 0, miBuffer->Length);
}

private: System::Void button_Led_13_ON_Click(System::Object^  sender, System::EventArgs^  e) {
cli::array<unsigned char> ^miBuffer = gcnew cli::array<unsigned char>(9);
miBuffer[0] = 0x4C; // ASCII letra "L".
miBuffer[1] = 0x65; // ASCII letra "e".
miBuffer[2] = 0x64; // ASCII letra "d".
miBuffer[3] = 0x5F; // ASCII letra "_".
miBuffer[4] = 0x31; // ASCII letra "1".
miBuffer[5] = 0x33; // ASCII letra "3".
miBuffer[6] = 0x5F; // ASCII letra "_".
miBuffer[7] = 0x4F; // ASCII letra "O".
miBuffer[8] = 0x4E; // ASCII letra "N".
serialPort1->Write(miBuffer, 0, miBuffer->Length);
}

private: System::Void button_Led_13_OFF_Click(System::Object^  sender, System::EventArgs^  e) {
cli::array<unsigned char> ^miBuffer = gcnew cli::array<unsigned char>(10);
miBuffer[0] = 0x4C; // ASCII letra "L".
miBuffer[1] = 0x65; // ASCII letra "e".
miBuffer[2] = 0x64; // ASCII letra "d".
miBuffer[3] = 0x5F; // ASCII letra "_".
miBuffer[4] = 0x31; // ASCII letra "1".
miBuffer[5] = 0x33; // ASCII letra "3".
miBuffer[6] = 0x5F; // ASCII letra "_".
miBuffer[7] = 0x4F; // ASCII letra "O".
miBuffer[8] = 0x46; // ASCII letra "F".
miBuffer[9] = 0x46; // ASCII letra "F".
serialPort1->Write(miBuffer, 0, miBuffer->Length);
}

// Declaramos un delegado.
delegate void Delegado(String ^ Recibidos);

private: Void serialPort1_DataReceived(Object^  sender, SerialDataReceivedEventArgs^  e) {
// Utilizremos un string como buffer de recepción.
String ^ Recibidos;

if (serialPort1->BytesToRead > 0){ // Si hay carácter que leer...

Recibidos = serialPort1->ReadExisting(); // Acumula los carácteres recibido.

// Invocamos y cargamos los bytes en rictTextBox_Mensajes.
Delegado ^ Actualizar = gcnew Delegado(this, &Form_Principal::ByteRecibidos);

this->Invoke(Actualizar, Recibidos);
}
}

void ByteRecibidos(String ^ Data){
// Los carácteres almacenado en 'Data' se depositan en richTextBox_Mensaje.
richTextBox_Mensajes->Text += Data;

// Selecciona la posición final para leer los mensajes entrantes.
richTextBox_Mensajes->SelectionStart = richTextBox_Mensajes->Text->Length;

// Mantiene el scroll en la entrada de cada mensaje.
richTextBox_Mensajes->ScrollToCaret();
}

};
}


Saludos.
Tutoriales Electrónica y PIC: http://electronica-pic.blogspot.com/