PLC&MLX90621&Teensy

PLC & MLX90621 & Teensy

Introduction

This page explains the development of an experiment to check the possibility of transmitting the data of thermal sensors through the power lines of a building.

I have used a PLC (Powerline Communication) module that provides the connectivity of anyone of several serial devices interfaces via power line. Both point-to-point and multi-point connection are possible. The data will be transmitted as TCP/IP packets over the power line. The PLC module chosen is the PLC-Stamp1 manufactured by I2SE. The module has TTL level serial interfaces. The galvanic isolation from the power is done on the module. The PLC chip QCA7000 from Qualcomm guarantees the compatibility with many other commercial powerline devices. This device is driven by the Freescale MK20DX256VM7 host controller. This PCB has a connector to have access to several GPIOs, an SPI port or an UART port of the micro-controller. For more information about the PLC-Stamp1 please click here.
Figure 1: Front and back side of the PLC-Stamp1 
The thermal sensor used is the MLX90621. It is a fully calibrated 16x4 pixels IR array in an industry standard 4-lead TO-39 package.  This sensor is accessible by the user controller through the I2C bus and have to be used external post processing of the thermal data. For more information about this sensor please click here.
Figure 2: Promotional picture of the MLX90621.
The I2C pins of the PLC-Stamp1 are not accessible and the Arduino Libraries for the sensor are written for the Teensy development board. So that, I have chosen the Teensy 3.1 for reading the sensor data and then send it through a serial port to the UART2 pins of the PLC-Stamp1. The Teensy 3.1 is a 32 Bit Arduino-Compatible microcontroller that it also has the Freescale MK20 controller. However, it is programmed using the Arduino IDE instead of the CodeWarrior in the case of the PLC device. For more information about Teensy please click here.
Figure 3: Teensy 3.1 pin-out.
Taking the reference of a picture of the PLC-Stamp1 datasheet:

Figure 4: An easy way to link two PLC-Stamp1s between each other and others electronic devices.
I have though the following block scheme to read the sensor and send the data to a computer using the powerlines of the building.

Figure 5: Block diagram of the experiment.

Objective

The aim of this experiment is to achieve the transmissions of the measurements of the MLX90621 through the powerlines of the buildings and visualizing the temperature data with a computer application.

Components

1x Teensy 3.1 [1]
2x PLC-STAMP1 [2]
1x Segger J-Link EDU [4]
1x USB-to-TTL converter [5]
2x MicroMatch cables [6]
2x MicroMatch vertical PCB connectors [7]
1x Thermal Sensor MLX90621 [8]
2x Green LED
2x Resistors 1K
1x diode 4001
2x Resistors 4K7
4x Resistors 10K
1x Ceramic capacitor 0.01uF
1x Electrolitic Capacitor 10uF
1x Electrolitic Capacitor 1uF
2x Ceramic Capacitor 0.1uF
1x LD1117 ST LDO 3.3V
Wires…

Development of the experiment

First part: Thermal Sensor MLX90621

The first step is to do the thermal sensor work. After looking up quite information on internet about how to connect this sensor, I came across with the conclusion that I need a powerful microcontroller to compute all the necessary calculations required by this sensor in order to get the correct information of temperature. I did several tries using an Arduino UNO and a MEGA. However, it is easier to start with something already done. In a forum I found a previous work with the sensor MLX90621 and the Teensy. This work can be downloaded here. This work reads the information of sensor using the I2C bus of the Freescale MK20 controller. The data are sent to a computer through the USB cable, where a Processing aplication draws and paints with different colors the infrared data.

This works can also be downloaded from this Github page:

These codes have been changed to my purpose and are exposed in the anexe. In the next picture is shown a circuit with the Teensy , the MLX90621, a connector to the PLC, one green LED and some buttons.

Figure 6: Teensy3.1 with the MLX90621.

The second part: Sending data by PLC

The data transmission through the electrical network is achieved using two PLC-Stamp1. One circuit receives the data from the  sensor by his serial port and transmits them with the Powerline Communication protocol. The other circuit receives the data sent by PLC and then, re-sends them by his serial port to the TTL to USB converter.

The circuit PLC-Stamp1 is a PCB designed by I2SE and it is thought for the communication between PLC devices. This PCB uses the communication standard Homeplug Green PHY for PLC, which encapsules the data with TCP/IP packets and send the information modulling the signal over the powerline.

This board can be divided in three parts in order to understand his operation. One part is the chip Qualcomm QCA7000 who manages the PLC communication. Another part it is the Freescale MK20DV256 microcontroller who is responsible to control the PLC chip using the SPI bus, the peripherals components that can be connected to their GPIO and the communication with the computer. This controller can be programmed using the integrated development enviroment called CodeWarrior. It is necessary a programmer. In my case, I have used the Segger J-Link-EDU. To finalize, it has a red connector where we can access to the GPIOs, a SPI and an UART of the microcontroller, the RESET pin of the PLC chip and the power supply pins 3V3 and GND.

The MK20 has been programmed to do the thing described in the first paragraph. The codes are at the end of this article.

Figura 7: photo of the firsts PLC tests.

Third part: painting the data on Windows

I have built a little circuit to get data from the UART of the  PLC-Stamp1 MicroMatch connector and send them using a TTL to USB converter. The PLC device works with 3.3 Volts and the TTL-USB converter ( it has the Prolific chip PL2303 ) works with 5 Volts. So, I need to convert the voltage levels of the serial communication in order to not to damage the PLC-Stamp1.

All the data is read by the Processing application and paints the temperature information of the 4x16 infrarred array on the screen of the computer. This application has been  also done by the same person who did the Arduino Sketch here.

Figure 8: Screenshot of the Processing application to visualize the data of the MLX90621.

Electric scheme of this experiment

Now, it is shown the connections among all the used devices to trasmit the data of a thermal sensor throth the powerline to a computer.
Figure 9: electric scheme of the MLX90621, Teensy and PLC-stamps1.

Results

In this test I have oriented the thermal sensor towards the door of the building. This sensor measures constantly the temperatures. The data are read by the microcontroller and are sent to the PLC device. The information is transmitted through the electric network of the building. In the next picture you can see the Teensy with the MLX90621 sensor connected to the PLC-Stamp1 and finally, this last circuit is linked to an electric plug. The whole is powered by a portable USB-Battery. The battery powers an Arduino which is used to provide the 3.3 volts to the PLC device. The PLC-Stamp1 gives also the 3.3 volts using the flat cable.

Figure 10: device that reads the thermal sensor and sends it through the powerline.

In a room of the same floor I put the PLC receiver. This circuit receives the data of the thermal sensor, processes them and re-sends them through the UART to the TTL to USB converter.

Figure 11:  PLC device receiver conneted to the electric network  and to a TTL-USB converter.
Then, the Processing aplication read the USB port and shows the temperature values on the screen.

Conclusion

I recommend the PLC-Stamp1 of I2SE for Powerline Comunnications developments. Perhaps, it is a little complicated to programming the MK20 controller using the CodeWarrior because the way of programming it is very different to Arduino but this device works perfectly, not like the Mamba shield.

References




Appendix

In this point it is exposed the codes used in this PLC system. It is not included some extra libraries necessaries for the compilation of the MK20 controller, only "main.c".

Arduino code for Teensy for reading the MLX90621

#include <Arduino.h>
#include <i2c_t3.h>
#include "MLX90621.h"

MLX90621 sensor; // create an instance of the Sensor class


void setup(){
  Serial.begin(9600);
  Serial1.begin(9600);
  pinMode(13,OUTPUT);
  sensor.initialise (16); // start the thermo cam with 16 frames per second
}
void loop()
{
  String texto="";
  char array[64];
  memset(array,'\0',64);
  digitalWrite(13,LOW);
  sensor.measure(); //get new readings from the sensor
  for(int y=0;y<4;y++)
  { //go through all the rows
    texto.concat("[");
    for(int x=0;x<16;x++){ //go through all the columns
      double tempAtXY= sensor.getTemperature(y+x*4); // extract the temperature at position x/y
      texto.concat((int)tempAtXY);  
      if (x<15)
      {
        texto.concat(",");
      }
    }
    texto.concat("]");
    if (y<3)
    {
      texto.concat("~");
    }
  }
  texto.concat("\n");
  //Serial.print(texto);
  //Serial.print(texto.length());
  int firstClosingBracket= texto.indexOf('~');
  int secondOpeningBracket = firstClosingBracket + 1;
  String SAA=texto.substring(0,secondOpeningBracket);
  SAA.toCharArray(array,SAA.length()+1);
  Serial1.print(SAA);
  //Serial.println(array);
  delay(10);
  int secondClosingBracket = texto.indexOf('~', secondOpeningBracket );
  int thirdOpeningBracket = secondClosingBracket + 1;
  String SBB=texto.substring(secondOpeningBracket,thirdOpeningBracket);
  Serial1.print(SBB);
  delay(10);
  digitalWrite(13,HIGH);
  int thirdClosingBracket = texto.indexOf('~', thirdOpeningBracket );
  int fourthOpeningBracket = thirdClosingBracket + 1;
  String SCC=texto.substring(thirdOpeningBracket,fourthOpeningBracket);
  Serial1.print(SCC);
  delay(10);
  int fourthClosingBracket = texto.indexOf('\0', fourthOpeningBracket );
  String SDD=texto.substring(fourthOpeningBracket,fourthClosingBracket);
  Serial1.print(SDD);
  delay(10);

}

Processing code for representing the data on the screen

/*
 *  VisualizeTemperatures.pde
 *
 *  A simple Processing Sketch which listens to a 
 *  Teensy 3.1+ Arduino board connected to a Melexis 90621
 *  and visualizes the temperature values measured by its
 *  4x16 matrix as a color matrix
 **/

import processing.serial.*;

Serial serialConnection;
String sensorReading;
Double temperatureToString;
float[][] drawTemperatures2D;
float[][] temperatures2D;
float H, S, B;
boolean waitFirstNewline;

void setup() {
  size(700, 220);
  waitFirstNewline = false;
  sensorReading="";
  serialConnection = new Serial(this, "COM75", 115200);
  serialConnection.bufferUntil('\n');
}

void serialEvent (Serial serialConnection) {
  sensorReading = serialConnection.readStringUntil('\n');
  if (sensorReading != null) {
    if (waitFirstNewline) {
      sensorReading=trim(sensorReading);
    } else {
      serialConnection.clear();
      waitFirstNewline = true;
    }
  }
}

void draw() {
  background(0);
  translate(35, 35);
  fill(255);
  noStroke();
  drawTemperatures2D = parseInput(sensorReading);
  if (drawTemperatures2D!=null) {
    for (int i = 15; i >= 0; i--) { 
      pushMatrix();
      for (int j = 0; j < 4; j++) {
        fill(getColor(((drawTemperatures2D[j][i]-37)/9)));
        rect(0, 0, 30, 30);
        textSize(10);
        fill(0);
        temperatureToString = (double) Math.round(drawTemperatures2D[j][i] * 10) / 10;
        text(temperatureToString.toString(), 4, 20);
        translate(0, 40);
      }
      popMatrix();
      translate(40, 0);
    }
  }
}

float[][] parseInput(String input) {
  String[] temperatureRows;
  temperatureRows = input.split("~");
  if (temperatureRows.length<4) return null;
  temperatures2D = new float[temperatureRows.length][];
  int i= 0;
  String[] temperatureCols;
  for (String row : temperatureRows) {
    row = row.substring(1, row.length()-1);
    temperatureCols = row.split(",");
    if (temperatureCols.length<16) return null;
    int j = 0;
    temperatures2D[i] = new float[temperatureCols.length];
    for (String col : temperatureCols) {
      try {
        temperatures2D[i][j++] = Float.parseFloat(col);
      }
      catch(NumberFormatException ex) {
        return null;
      }
    }
    i++;
  }
  return temperatures2D;
}

color getColor(float power)
{
  colorMode(HSB, 1.0);
  H = (1-power) * 0.4;
  S = 0.9;
  B = 0.9;

  return color(H, S, B);
}

PLC-Stamp1 code transmitter

/** ###################################################################
**     Filename    : ProcessorExpert.c
**     Project     : ProcessorExpert
**     Processor   : MK20DX256VLL7
**     Version     : Driver 01.01
**     Compiler    : GNU C Compiler
**     Date/Time   : 2013-03-19, 07:47, # CodeGen: 0
**     Abstract    :
**         Main module.
**         This module contains user's application code.
**     Settings    :
**     Contents    :
**         No public methods
**
** ###################################################################*/
/* MODULE ProcessorExpert */
//PLACA A!!!

/* Including needed modules to compile this module/procedure */
#include "Cpu.h"
#include "Events.h"
#include "LED1.h"
#include "LED2.h"
#include "LED3.h"
#include "LED4.h"
#include "SM1.h"
#include "SPI_INTR.h"
#include "TIMER.h"
#include "CON_X3_6.h"
#include "CON_X3_5.h"
#include "CON_X3_4.h"
#include "CON_X3_3.h"
#include "QCA7K_RESET.h"
#include "AS1.h"
/* Including shared modules, which are used for whole project */
#include "PE_Types.h"
#include "PE_Error.h"
#include "PE_Const.h"
#include "IO_Map.h"
/* Librerias añadidas por mi */
#include "UART_PDD.h"

/* User includes (#include below this line is not maintained by Processor Expert) */
#include <string.h>
#include <stdio.h>

#include "qca_spi.h"
#include "qca_block.h"

#include "libmme.h"
#include "libmme_over_spi.h"

LDD_TDeviceData *TimerData;
LDD_TDeviceData *spiMasterDevData;
LDD_TDeviceData *spiIntrDevData;

LDD_TDeviceData *Led1Data;    /* LED1 */
LDD_TDeviceData *Led2Data;    /* LED2 */
LDD_TDeviceData *Led3Data;    /* LED3 */
LDD_TDeviceData *Led4Data;    /* LED4 */

LDD_TDeviceData *Con_X3_3Data; /* Ext Con X3 Pin 3 */
LDD_TDeviceData *Con_X3_4Data; /* Ext Con X3 Pin 4 */
LDD_TDeviceData *Con_X3_5Data; /* Ext Con X3 Pin 5 */
LDD_TDeviceData *Con_X3_6Data; /* Ext Con X3 Pin 6 */
LDD_TDeviceData *AS1;          /* Ext Con X3 Pin 7 & 8 */
LDD_TUserData   *AS1_Data;     /* Ext Con X3 Pin 7 & 8 */

QCASPI_DEVICE qca;             // SPI driver
uint8_t sendMme;               // request counter for MME broadcast
uint8_t qcaTimeout;            // counter flag for SPI driver timeout (50 ms)
uint8_t  led2Duration;         // counter for LED2 ( 0 = LED off, >0 = LED on)
uint16_t validMmeDuration;     // counter for LED4 ( 0 = LED off, >0 = LED on)

/**
 * Aquí añado mis cosas
 */
uint8_t RELLENO=63;

/**
 *   This function sends a mesagge to all the devices of the net with the
 *   information read from the UART2.
 *   @brief function to send a I2SE GPIO indication MME
 *   @param libmme_config libmme structure that stores all internal settings
 *   @param oda original destination address (broadcast MAC)
 *   @param osa original source address (your MAC)
 *   @retval >=0 the return value of libmme_over_spi_if_output function
 */
int send_uart2_reading(struct libmme* libmme_config, uint8_t oda[LIBMME_MAC_ADDRESS_SIZE], uint8_t osa[LIBMME_MAC_ADDRESS_SIZE], uint8_t padding[RELLENO+1])
{      
        uint8_t gpioStates=0xFF;
        uint8_t gpioDirections=0xFF;
        static uint8_t mme[] = {
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ODA              [0,5]
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OSA (variable)   [6,11]
                 0x88, 0xE1,                         // MTYPE            [12,13]
                 0x01,                               // MMV              [14]
                 0x02, 0xA0,                         // MMTYPE           [15,16]
                 0x00, 0x00,                         // FMI              [17,18]
                 0x00, 0x01, 0x87,                   // OUI              [19,21]
                 0x00,                               // MME_SUBVER       [22]
                 0x00,                               // STATE (variable) [23]
                 0x00,                               // DIR (variable)   [24]
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //[25] PADDING
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //[31]
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //[37]
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //[43]
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //[49]
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //[55]
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //[61]
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //[67]
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //[73]
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //[79]
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //[85:90]
        };
        struct libmme_buffer output_data;
       
        memcpy(&mme[0], oda, LIBMME_MAC_ADDRESS_SIZE); // set original destination address
        memcpy(&mme[6], osa, LIBMME_MAC_ADDRESS_SIZE); // set original source address
        mme[23] = 0x3F & gpioStates;                   // set GPIO states
        mme[24] = 0xC0 | gpioDirections;               // set GPIO directions
        memcpy(&mme[25], padding, sizeof(uint8_t)*(RELLENO+1));               // set original source address
        output_data.data = mme;
        output_data.next = sizeof(mme);
        output_data.length = sizeof(mme);
       
        return libmme_over_spi_if_output(libmme_config, &output_data); // send MME over SPI to QCA7K
}

/**
 *   This function is a replacement for libmme_over_spi_if_input to handle MMEs
 *   direct without libmme.
 *  
 *   @brief function to handle MMEs direct
 *   @param netif libmme structure that stores all internal settings
 *   @retval #LIBMME_OVER_SPI_RESULT_OK when MME is handled or frame is ignored
 *   @retval #LIBMME_OVER_SPI_RESULT_NULL_POINTER when netif is NULL
 *   @retval #LIBMME_OVER_SPI_RESULT_RUNTIME_ERROR when actually there is not input frame
 *   @retval #LIBMME_OVER_SPI_RESULT_INVALID_PARAM when input frame is invalid
 */
uint8_t mme_input(void *netif)
{
  struct libmme* libmme_config = (struct libmme*) netif;
  uint16_t receive_length = 0;
  uint8_t *receive_data;
 
  extern uint16_t validMmeDuration;
 
  if (netif == NULL)
  {
          return LIBMME_OVER_SPI_RESULT_NULL_POINTER;
  }
 
  receive_data = qcaspi_get_input_frame((QCASPI_DEVICE*)libmme_config->low_level_interface, &receive_length);
  if (receive_data == NULL)
  {
          return LIBMME_OVER_SPI_RESULT_RUNTIME_ERROR;
  }
 
  if (receive_length < 25) // check length
  {
          return LIBMME_OVER_SPI_RESULT_OK; // ignore frame, too small length
  }

  if ((receive_data[12] != 0x88) ||
          (receive_data[13] != 0xE1)) // check MTYPE
  {
          return LIBMME_OVER_SPI_RESULT_OK; // ignore frame, unknown MTYPE
  }
 
  if (receive_data[14] != 0x01) // check MMV
  {
          return LIBMME_OVER_SPI_RESULT_OK; // ignore frame, unknown MMV
  }
 
  if ((receive_data[15] != 0x02) ||
          (receive_data[16] != 0xA0)) // check MMTYPE
  {
          return LIBMME_OVER_SPI_RESULT_OK; // ignore frame, unknown MMTYPE
  }

  // ignore FMI
 
  if ((receive_data[19] != 0x00) ||
          (receive_data[20] != 0x01) ||
          (receive_data[21] != 0x87)) // check I2SE OUI
  {
          return LIBMME_OVER_SPI_RESULT_OK; // ignore frame, unknown OUI
  }
 
  if (receive_data[22] != 0x00) // check MME_SUBVER
  {
          return LIBMME_OVER_SPI_RESULT_INVALID_PARAM; // invalid MME_SUBVER
  }
 
  if (receive_data[23] & 0xC0) // check STATE
  {
          return LIBMME_OVER_SPI_RESULT_INVALID_PARAM; // invalid STATE
  }
    
  if ((receive_data[24] & 0xC0) != 0xC0) // check DIR
  {
          return LIBMME_OVER_SPI_RESULT_INVALID_PARAM; // invalid DIR
  }
  
  if (receive_data[25] =='[') // check confirmation parameter
          CON_X3_4_ToggleFieldBits(Con_X3_4Data, Con_X3_4Pin, 1);//Put HIGH GPIO4
  else
          CON_X3_4_ClearFieldBits(Con_X3_4Data, Con_X3_4Pin, 1);//Put LOW GPIO4
   
  validMmeDuration = 1; // switch LED4 on
  return LIBMME_OVER_SPI_RESULT_OK;
}

/************************************************************************************
 *
 *   @brief function to enable all interrupts
 *
 ************************************************************************************/
void enableInt(void)
{
        __EI();
}

/************************************************************************************
 *
 *   @brief function to disable all interrupts
 *
 ************************************************************************************/
void disableInt(void)
{
        __DI();
}

/*lint -save  -e970 Disable MISRA rule (6.3) checking. */
int main(void)
/*lint -restore Enable MISRA rule (6.3) checking. */
{
        uint8_t originalDstAddr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // broadcast address
                
        /* Please replace with MAC hardware address MK20 on the PLC-Stamp 1 label. */
        uint8_t originalSrcAddr[6] = { 0x00, 0x01, 0x87, 0x04, 0x05, 0x8B };//PLC-Stamp A
       
        uint32_t ticks;
        uint8_t i;
        struct libmme libmme_config;
        int result;

        uint8_t data[RELLENO+1];
        uint8_t InputBuffer[RELLENO];
        memset (data,'\0',sizeof(data));
        memset (InputBuffer,'\0',sizeof(InputBuffer));
       
        /*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
        PE_low_level_init();
        /*** End of Processor Expert internal initialization.                    ***/
       
        /* Init all 4 LEDs */
        Led1Data = LED1_Init(NULL);
        Led2Data = LED2_Init(NULL);
        Led3Data = LED3_Init(NULL);
        Led4Data = LED4_Init(NULL);
       
        /* Init GPIOs on external connector X3 */
        Con_X3_3Data = CON_X3_3_Init(NULL);
        Con_X3_4Data = CON_X3_4_Init(NULL);
        Con_X3_5Data = CON_X3_5_Init(NULL);
        Con_X3_6Data = CON_X3_6_Init(NULL);
       
        spiMasterDevData = SM1_Init(&qca);    // Init spi master interface with pointer to the driver
        spiIntrDevData = SPI_INTR_Init(&qca); // Init spi interrupt gpio with pointer to the driver
       
        /* Init SPI driver with attribute set 0 (default: Width = 8 bit, MSB first, CPOL=1, CPHA=1, No parity, No CS toggle)
         * and chip 0 (chip select = active low) */
        qcaspi_init(&qca,
                                    spiMasterDevData,         // spi master device
                                    spiIntrDevData,           // spi interrupt gpio device
                                    SpiIntr,                  // spi interrupt gpio number
                                    SM1_ReceiveBlock,         // pointer to spi receive function
                                    SM1_SendBlock,            // pointer to spi send function
                                    SM1_SelectConfiguration,  // pointer to spi config function
                                    SPI_INTR_GetFieldValue,   // pointer to get level of spi interrupt
                                    disableInt,               // pointer to disable all interrupts
                                    enableInt);               // pointer to enable all interrupts
       
        libmme_init(&libmme_config, &qca, libmme_over_spi_if_output, NULL, NULL);
        libmme_over_spi_if_init(&libmme_config);
        qcaspi_set_interface(&qca, &libmme_config);
        qcaspi_set_interface_input(&qca, mme_input); // use custom frame input function instead from libmme_over_spi
       
        //Aviso luminoso de que la placa se esta inicializando
        for (i = 0; i < 10; i++)
        {
          /* Switch all LEDs on */
          LED1_ClearFieldBits(Led1Data, Led1Pin, 1);
          LED2_ClearFieldBits(Led2Data, Led2Pin, 1);
          LED3_ClearFieldBits(Led3Data, Led3Pin, 1);
          LED4_ClearFieldBits(Led4Data, Led4Pin, 1);
          CON_X3_4_SetFieldBits(Con_X3_4Data, Con_X3_4Pin, 1);//Put HIGH GPIO4
         
          for (ticks = 0; ticks < 2000000;)
          {
                   ticks++;
          }
         
          /* Switch all LEDs off */
          LED1_SetFieldBits(Led1Data, Led1Pin, 1);
          LED2_SetFieldBits(Led2Data, Led2Pin, 1);
          LED3_SetFieldBits(Led3Data, Led3Pin, 1);
          LED4_SetFieldBits(Led4Data, Led4Pin, 1);
          CON_X3_4_ClearFieldBits(Con_X3_4Data, Con_X3_4Pin, 1);//Put LOW GPIO4
          for (ticks = 0; ticks < 2000000;)
          {
                   ticks++;
          }
        }
       
        /* Start timer (interval = 50 ms) */
        TimerData = TIMER_Init(&qca);
       
        AS1=AS1_Init(AS1_Data);//Inicializa el puerto serie
        /* application main loop */
        while (1)
        {               
                 if (qcaTimeout > 0)
                 {      
                         qcaTimeout--;
                         sendMme++;
                 }
                
                 if (qcaspi_is_sync(&qca))// If synchronisation is ready, switch on LED3            
                         LED3_ClearFieldBits(Led3Data, Led3Pin, 1);
                 else
                         LED3_SetFieldBits(Led3Data, Led3Pin, 1);
         
                 (void) qcaspi_process(&qca); // async spi task
                
                 if ((sendMme > 0) && qcaspi_is_sync(&qca))
                 {
                         if(AS1_ReceiveBlock(AS1,InputBuffer, sizeof(InputBuffer))==ERR_OK)//TODO
                         {
                                  LED1_ToggleFieldBits(Led1Data, Led1Pin, 1);
                                  memcpy( &data[0], InputBuffer, sizeof(InputBuffer) );
                             result = send_uart2_reading(&libmme_config,originalDstAddr,originalSrcAddr,data); 
                                          if (result == LIBMME_OVER_SPI_RESULT_OK)
                                          {
                                                   led2Duration = 1; // switch LED2 on
                                                   sendMme--;   
                                           }                             
                         }
                 }
        }
       
  /*** Don't write any code pass this line, or it will be deleted during code generation. ***/
  /*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T MODIFY THIS CODE!!! ***/
  #ifdef PEX_RTOS_START
    PEX_RTOS_START();                  /* Startup of the selected RTOS. Macro is defined by the RTOS component. */
  #endif
  /*** End of RTOS startup code.  ***/
  /*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/
  for(;;){}
  /*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/

/* END ProcessorExpert */
/*
** ###################################################################
**
**     This file was created by Processor Expert 10.0 [05.03]
**     for the Freescale Kinetis series of microcontrollers.
**
** ###################################################################
*/ 

PLC-Stamp1 receiver

/** ###################################################################
**     Filename    : ProcessorExpert.c
**     Project     : ProcessorExpert
**     Processor   : MK20DX256VLL7
**     Version     : Driver 01.01
**     Compiler    : GNU C Compiler
**     Date/Time   : 2013-03-19, 07:47, # CodeGen: 0
**     Abstract    :
**         Main module.
**         This module contains user's application code.
**     Settings    :
**     Contents    :
**         No public methods
**        
**
** ###################################################################*/
/* MODULE ProcessorExpert */
//PLACA B!!!

/* Including needed modules to compile this module/procedure */
#include "Cpu.h"
#include "Events.h"
#include "LED1.h"
#include "LED2.h"
#include "LED3.h"
#include "LED4.h"
#include "SM1.h"
#include "SPI_INTR.h"
#include "TIMER.h"
#include "CON_X3_6.h"
#include "CON_X3_5.h"
#include "CON_X3_4.h"
#include "CON_X3_3.h"
#include "QCA7K_RESET.h"
#include "AS1.h"
/* Including shared modules, which are used for whole project */
#include "PE_Types.h"
#include "PE_Error.h"
#include "PE_Const.h"
#include "IO_Map.h"
#include "UART_PDD.h"

/* User includes (#include below this line is not maintained by Processor Expert) */
#include <string.h>
#include <stdio.h>

#include "qca_spi.h"
#include "qca_block.h"

#include "libmme.h"
#include "libmme_over_spi.h"

LDD_TDeviceData *TimerData;
LDD_TDeviceData *spiMasterDevData;
LDD_TDeviceData *spiIntrDevData;

LDD_TDeviceData *Led1Data;    /* LED1 */
LDD_TDeviceData *Led2Data;    /* LED2 */
LDD_TDeviceData *Led3Data;    /* LED3 */
LDD_TDeviceData *Led4Data;    /* LED4 */

LDD_TDeviceData *Con_X3_3Data; /* Ext Con X3 Pin 3 */
LDD_TDeviceData *Con_X3_4Data; /* Ext Con X3 Pin 4 */
LDD_TDeviceData *Con_X3_5Data; /* Ext Con X3 Pin 5 */
LDD_TDeviceData *Con_X3_6Data; /* Ext Con X3 Pin 6 */
LDD_TDeviceData *AS1;                
LDD_TUserData   *AS1_Data;    

QCASPI_DEVICE qca;             // SPI driver
uint8_t sendMme;               // request counter for MME broadcast
uint8_t qcaTimeout;            // counter flag for SPI driver timeout (50 ms)
uint8_t  led2Duration;         // counter for LED2 ( 0 = LED off, >0 = LED on)
uint16_t validMmeDuration;     // counter for LED4 ( 0 = LED off, >0 = LED on)

/**
 * Aquí añado mis cosas
 */
uint8_t RELLENO=64,tramaRecibida=0;

/**
 *   This function sends a confirmation message
 *
 *   @brief function to send a I2SE GPIO indication MME
 *   @param libmme_config libmme structure that stores all internal settings
 *   @param oda original destination address (broadcast MAC)
 *   @param osa original source address (your MAC)
 *   @param gpioDirections GPIO directions of the last 6 connector pins (0 = input / 1 = output)
 *   @param gpioStates last 6 levels of the GPIO pins as flags (0 = low, 1 = high)
 *   @retval >=0 the return value of libmme_over_spi_if_output function
 */
int send_confirmation(struct libmme* libmme_config, uint8_t oda[LIBMME_MAC_ADDRESS_SIZE], uint8_t osa[LIBMME_MAC_ADDRESS_SIZE])
{
        uint8_t gpioStates=0xFF;
        uint8_t gpioDirections=0xFF;             
        static uint8_t mme[] = {
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ODA              [0,5]
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // OSA (variable)   [6,11]
                 0x88, 0xE1,                         // MTYPE            [12,13]
                 0x01,                               // MMV              [14]
                 0x02, 0xA0,                         // MMTYPE           [15,16]
                 0x00, 0x00,                         // FMI              [17,18]
                 0x00, 0x01, 0x87,                   // OUI              [19,21]
                 0x00,                               // MME_SUBVER       [22]
                 0x00,                               // STATE (variable) [23]
                 0x00,                               // DIR (variable)   [24]
                 '[', 0x00, 0x00, 0x00, 0x00, 0x00,
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                 0x00, 0x00, 0x00, 0x00, 0x00        // Padding          [25,59]
        };
        struct libmme_buffer output_data;
       
        memcpy(&mme[0], oda, LIBMME_MAC_ADDRESS_SIZE); // set original destination address
        memcpy(&mme[6], osa, LIBMME_MAC_ADDRESS_SIZE); // set original source address
        mme[23] = 0x3F & gpioStates;                   // set GPIO states
        mme[24] = 0xC0 | gpioDirections;               // set GPIO directions
        output_data.data = mme;
        output_data.next = sizeof(mme);
        output_data.length = sizeof(mme);
        return libmme_over_spi_if_output(libmme_config, &output_data); // send MME over SPI to QCA7K
}

/**
 *   This function is a replacement for libmme_over_spi_if_input to handle MMEs
 *   direct without libmme.
 *  
 *   @brief function to handle MMEs direct
 *   @param netif libmme structure that stores all internal settings
 *   @retval #LIBMME_OVER_SPI_RESULT_OK when MME is handled or frame is ignored
 *   @retval #LIBMME_OVER_SPI_RESULT_NULL_POINTER when netif is NULL
 *   @retval #LIBMME_OVER_SPI_RESULT_RUNTIME_ERROR when actually there is not input frame
 *   @retval #LIBMME_OVER_SPI_RESULT_INVALID_PARAM when input frame is invalid
 */
uint8_t mme_input(void *netif)
{
  struct libmme* libmme_config = (struct libmme*) netif;
  uint16_t receive_length = 0;
  uint8_t *receive_data;
 
  extern uint16_t validMmeDuration;
 
  if (netif == NULL)
  {
          return LIBMME_OVER_SPI_RESULT_NULL_POINTER;
  }
 
  receive_data = qcaspi_get_input_frame((QCASPI_DEVICE*)libmme_config->low_level_interface, &receive_length);
  if (receive_data == NULL)
  {
          return LIBMME_OVER_SPI_RESULT_RUNTIME_ERROR;
  }
 
  if (receive_length < 25) // check length
  {
          return LIBMME_OVER_SPI_RESULT_OK; // ignore frame, too small length
  }

  if ((receive_data[12] != 0x88) ||
          (receive_data[13] != 0xE1)) // check MTYPE
  {
          return LIBMME_OVER_SPI_RESULT_OK; // ignore frame, unknown MTYPE
  }
 
  if (receive_data[14] != 0x01) // check MMV
  {
          return LIBMME_OVER_SPI_RESULT_OK; // ignore frame, unknown MMV
  }
 
  if ((receive_data[15] != 0x02) ||
          (receive_data[16] != 0xA0)) // check MMTYPE
  {
          return LIBMME_OVER_SPI_RESULT_OK; // ignore frame, unknown MMTYPE
  }

  // ignore FMI
 
  if ((receive_data[19] != 0x00) ||
          (receive_data[20] != 0x01) ||
          (receive_data[21] != 0x87)) // check I2SE OUI
  {
          return LIBMME_OVER_SPI_RESULT_OK; // ignore frame, unknown OUI
  }
 
  if (receive_data[22] != 0x00) // check MME_SUBVER
  {
          return LIBMME_OVER_SPI_RESULT_INVALID_PARAM; // invalid MME_SUBVER
  }
 
  if (receive_data[23] & 0xC0) // check STATE
  {
          return LIBMME_OVER_SPI_RESULT_INVALID_PARAM; // invalid STATE
  }
   
  if ((receive_data[24] & 0xC0) != 0xC0) // check DIR
  {
          return LIBMME_OVER_SPI_RESULT_INVALID_PARAM; // invalid DIR
  }
 
  uint8_t *mensaje = receive_data + 25;
  AS1_SendBlock(AS1,mensaje,sizeof(uint8_t)*strlen(mensaje));
  tramaRecibida=1;
  validMmeDuration = 1; // switch LED4 on
  return LIBMME_OVER_SPI_RESULT_OK;
}
/************************************************************************************
 *
 *   @brief function to enable all interrupts
 *
 ************************************************************************************/
void enableInt(void)
{
        __EI();
}

/************************************************************************************
 *
 *   @brief function to disable all interrupts
 *
 ************************************************************************************/
void disableInt(void)
{
        __DI();
}

int main(void)
{
        uint8_t originalDstAddr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // broadcast address
                
        /* Please replace with MAC hardware address MK20 on the PLC-Stamp 1 label. */
        uint8_t originalSrcAddr[6] = { 0x00, 0x01, 0x87, 0x04, 0x05, 0x87 };//PLC-Stamp B
       
        uint32_t ticks;
        uint8_t i;
        struct libmme libmme_config;
        int result;
       
        /*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
        PE_low_level_init();
        /*** End of Processor Expert internal initialization.                    ***/
       
        /* Init all 4 LEDs */
        Led1Data = LED1_Init(NULL);
        Led2Data = LED2_Init(NULL);
        Led3Data = LED3_Init(NULL);
        Led4Data = LED4_Init(NULL);
       
        /* Init GPIOs on external connector X3 */
        Con_X3_3Data = CON_X3_3_Init(NULL);
        Con_X3_4Data = CON_X3_4_Init(NULL);
        Con_X3_5Data = CON_X3_5_Init(NULL);
        Con_X3_6Data = CON_X3_6_Init(NULL);
       
        spiMasterDevData = SM1_Init(&qca);    // Init spi master interface with pointer to the driver
        spiIntrDevData = SPI_INTR_Init(&qca); // Init spi interrupt gpio with pointer to the driver
       
        /* Init SPI driver with attribute set 0 (default: Width = 8 bit, MSB first, CPOL=1, CPHA=1, No parity, No CS toggle)
         * and chip 0 (chip select = active low) */
        qcaspi_init(&qca,
                                    spiMasterDevData,         // spi master device
                                    spiIntrDevData,           // spi interrupt gpio device
                                    SpiIntr,                  // spi interrupt gpio number
                                    SM1_ReceiveBlock,         // pointer to spi receive function
                                    SM1_SendBlock,            // pointer to spi send function
                                    SM1_SelectConfiguration,  // pointer to spi config function
                                    SPI_INTR_GetFieldValue,   // pointer to get level of spi interrupt
                                    disableInt,               // pointer to disable all interrupts
                                    enableInt);               // pointer to enable all interrupts
       
        libmme_init(&libmme_config, &qca, libmme_over_spi_if_output, NULL, NULL);
        libmme_over_spi_if_init(&libmme_config);
        qcaspi_set_interface(&qca, &libmme_config);
        qcaspi_set_interface_input(&qca, mme_input); // use custom frame input function instead from libmme_over_spi
       
        for (i = 0; i < 10; i++)
        {
          /* Switch all LEDs on */
          LED1_ClearFieldBits(Led1Data, Led1Pin, 1);
          LED2_ClearFieldBits(Led2Data, Led2Pin, 1);
          LED3_ClearFieldBits(Led3Data, Led3Pin, 1);
          LED4_ClearFieldBits(Led4Data, Led4Pin, 1);
          CON_X3_6_SetFieldBits(Con_X3_6Data, Con_X3_6Pin, 1);//Put HIGH GPIO6
          for (ticks = 0; ticks < 2000000;)
          {
                   ticks++;
          }
         
          /* Switch all LEDs off */
          LED1_SetFieldBits(Led1Data, Led1Pin, 1);
          LED2_SetFieldBits(Led2Data, Led2Pin, 1);
          LED3_SetFieldBits(Led3Data, Led3Pin, 1);
          LED4_SetFieldBits(Led4Data, Led4Pin, 1);
          CON_X3_6_ClearFieldBits(Con_X3_6Data, Con_X3_6Pin, 1);//Put LOW GPIO6
          for (ticks = 0; ticks < 2000000;)
          {
                   ticks++;
          }
        }
       
        /* Start timer (interval = 50 ms) */
        TimerData = TIMER_Init(&qca);
       
        AS1=AS1_Init(AS1_Data);
        /* application main loop */
        while (1)
        {               
                 /* poll every 50 ms DIP switches and GPIOs */
                 if (qcaTimeout > 0)
                 {
                         qcaTimeout--;
                         sendMme++;
                 }
                
                 if (qcaspi_is_sync(&qca))
                         LED3_ClearFieldBits(Led3Data, Led3Pin, 1);
                 else
                         LED3_SetFieldBits(Led3Data, Led3Pin, 1);
                          
                 (void) qcaspi_process(&qca); // async spi task
                
                 if ((sendMme > 0) && qcaspi_is_sync(&qca))
                 {
                         if(tramaRecibida==1)
                         {      
                                  result = send_confirmation(&libmme_config,originalDstAddr,originalSrcAddr); // broadcast MME
                                  if (result == LIBMME_OVER_SPI_RESULT_OK)
                                  {
                                          sendMme--;
                                          led2Duration = 1; // switch LED2 on
                                  }
                                  tramaRecibida=0;
                         }
                 }
        }
       
  /*** Don't write any code pass this line, or it will be deleted during code generation. ***/
  /*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T MODIFY THIS CODE!!! ***/
  #ifdef PEX_RTOS_START
    PEX_RTOS_START();                  /* Startup of the selected RTOS. Macro is defined by the RTOS component. */
  #endif
  /*** End of RTOS startup code.  ***/
  /*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/
  for(;;){}
  /*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/

/* END ProcessorExpert */
/*
** ###################################################################
**
**     This file was created by Processor Expert 10.0 [05.03]
**     for the Freescale Kinetis series of microcontrollers.
**
** ###################################################################
*/