Input devices

This week will work on implementation reading of sensor data. And visualizing of received data using the some chart and data visualization library.

software / sites used during this week

XCode
I will try to use Mac OS X C development IDE to programm Attiny
X-AVR
X-AVR is an XCode template for generating AVR C projects. github link here. Very comfortable way to develop and build using this template (better then crosspack brut).
Highcharts
Chart library with many different static and dynamic charts. Accessible here
d3js
Opensource chart library "Data-Driven Documents". Accessible here
ArduinoIDE
Arduino IDE to programm the arduino boards.

hardware

Arduino UNO / nano
Arduino UNO and nano with components and test board that will permit to fast develop and validate functionality of code. As well used to drive motors of "L-Axis" in first version.
Attiny45
1$ 8 pins low power avr controller. Nice for sensor capture projects.
IR Distance Sensor
Just for test will be used IR distance sensor.
Moisture sensor
Will use existing moisture sensor and probably will try to build own one.

All files that I have demonstrated below are also available in this zip file

Assignments

Reading data from sensor

Let try from simple and let connect IR sensor using the Arduino UNO to get first prototype. Arduino IDE contain libraries that simplify work with Serial port. We loose in program size (and it may be critical in real realizations using the attiny controllers, but in case of arduino we have ~32K memory to use so it big enough and give a win on development speed)

Let connect the IR sensor to analog input port and let do simple C program that read data from sensor each 2 seconds (to avoid many noise) and send data to serial. Below you can see the arduino sketch and the output via seral.

#include <SharpIR.h>

#define ir A0
#define model 1080

SharpIR SharpIR(ir, model);
unsigned long starttime=millis();

void setup() {
  Serial.begin(9600);
}

void loop() {
  delay(2000);
  unsigned long pepe1=millis();

  int dis=SharpIR.distance();

  Serial.print("Distance: ");
  Serial.println(dis);

  Serial.print("Runing since: ");
  unsigned long r_since=millis()-starttime;
  Serial.println(r_since);

}
                  
    Distance: 71
    Runing since: 2003
    Distance: 64
    Runing since: 4007
    Distance: 37
    Runing since: 6011
    Distance: 34
    Runing since: 8015
    Distance: 11
    Runing since: 10019
    Distance: 8
    Runing since: 12024
    Distance: 7
    Runing since: 14028
    Distance: 7
    Runing since: 16032
    Distance: 10
    Runing since: 18037
    Distance: 9
    Runing since: 20041
    Distance: 22
    Runing since: 22045
    Distance: 21
    Runing since: 24049
    Distance: 176
    Runing since: 26054
    Distance: 460
    Runing since: 28058
    Distance: 11
                  

Now let use same program and change a little output so we can see the kind of "timestamp" of program running and it could be used to place values to timeline

unsigned long r_since=millis()-starttime;
 if (r_since<10) Serial.print(B0);
 if (r_since<100) Serial.print(B0);
 if (r_since<1000) Serial.print(B0);
 if (r_since<10000) Serial.print(B0);
 if (r_since<100000) Serial.print(B0);
 if (r_since<1000000) Serial.print(B0);
 if (r_since<10000000) Serial.print(B0);

 Serial.print(r_since);
 Serial.print(" ");
 Serial.println(dis);
                                
0006011 38
0008015 37
0010019 26
0012023 21
0014028 855
0016032 39
0018036 30
0020040 13

                                  

Now we can use this data for visualization. Let see it a little later.

Some attiny code

I took as example one of the provided code from the current week assignment. I tried to improve a little the Serial communication and integrated assembler library to same project. Goal was to use in same project the assembler code in parallel with the C (using the standard makefile and the XCode as IDE). So here what I have now.


      #include <avr/io.h>
      ; correct for avr/io.h 0x20 port offset for io instructions
      #define UART_Port (PORTB-0x20)
      #define UART_Tx 0

      #define bitcnt r18
      #define delayArg 19

      #if F_CPU == 8000000L
        #warning Using 8Mhz CPU timing
        #define TXDELAY 21
      #elif F_CPU == 16000000L
        #warning Using 16Mhz CPU timing
        #define TXDELAY 44
      #elif F_CPU == 1000000L
        #warning Using 16Mhz CPU timing
        #define TXDELAY 3
      #else
        #define TXDELAY 21
      #endif

      .global TxByte
      ; transmit byte in r24 - 12 instructions
      ; calling code must set Tx line to idle state (high) or 1st byte may be lost
      ; i.e. PORTB |= (1<
                            
Let add into Makefile following line that will include in compiling process all .S files
      SRC = $(wildcard *.c)
      OBJDIR = Builds
      ASRC = $(wildcard *.S)

    
Let create the header file BasicSerial.h
      void TxByte(char);
    
And now C code including this header with some improvements.
      //
      // hello.load.45.c
      //
      // step response loading hello-world
      //    9600 baud FTDI interface
      //
      // Neil Gershenfeld
      // 10/27/10
      //
      // (c) Massachusetts Institute of Technology 2010
      // This work may be reproduced, modified, distributed,
      // performed, and displayed for any purpose. Copyright is
      // retained and must be preserved. The work is provided
      // as is; no warranty is provided, and users accept all
      // liability.
      //

      #include <avr/io.h>
      #include <util/delay.h>
      #include <stdlib.h>

      #include "BasicSerial.h"

      #define output(directions,pin) (directions |= pin) // set port direction for output
      #define set(port,pin) (port |= pin) // set port pin
      #define clear(port,pin) (port &= (~pin)) // clear port pin
      #define pin_test(pins,pin) (pins & pin) // test for port pin
      #define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set
      #define bit_delay_time 102 // bit delay for 9600 with overhead
      #define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay
      #define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay
      #define charge_delay_1() _delay_us(1) // charge delay 1
      #define charge_delay_2() _delay_us(10) // charge delay 2
      #define charge_delay_3() _delay_us(100) // charge delay 3
      #define settle_delay() _delay_us(100) // settle delay
      #define char_delay() _delay_ms(10) // char delay

      #define serial_port PORTB
      #define serial_direction DDRB
      #define serial_pin_out (1 << PB2)
      #define charge_port PORTB
      #define charge_direction DDRB
      #define charge_pin (1 << PB3)


      void serOut(const char* str)
      {
          while (*str) TxByte (*str++);
      }


      void collect_step(unsigned char *up_lo_p, unsigned char *up_hi_p, unsigned char *down_lo_p, unsigned char *down_hi_p, char* snum) {
          //
          // initiate conversion
          //
          ADCSRA |= (1 << ADSC);
          //
          // wait for completion
          //
          while (ADCSRA & (1 << ADSC))
              ;
          //
          // save result
          //
          *up_lo_p = ADCL;
          *up_hi_p = ADCH;
          //
          // settle, discharge, and wait 2
          //
          settle_delay();
          clear(charge_port, charge_pin);
          charge_delay_2();
          //
          // initiate conversion
          //
          ADCSRA |= (1 << ADSC);
          //
          // wait for completion
          //
          while (ADCSRA & (1 << ADSC))
              ;
          //
          // save result
          //
          *down_lo_p = ADCL;
          *down_hi_p = ADCH;
          //
          // send result
          //
          itoa(*up_lo_p,snum,10);
          serOut(snum);
          itoa (*up_hi_p,snum,10);
          serOut(snum);
          itoa (*down_lo_p,snum,10);
          serOut(snum);
          itoa (*down_hi_p,snum,10);
          serOut(snum);
      }

      int main(void) {
          //
          // main
          //
          serOut("Starting\n");
          static unsigned char up_lo,up_hi,down_lo,down_hi;
          //
          // set clock divider to /1
          //
          CLKPR = (1 << CLKPCE);
          CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
          //
          // initialize output pins
          //
          set(serial_port, serial_pin_out);
          output(serial_direction, serial_pin_out);
          clear(charge_port, charge_pin);
          output(charge_direction, charge_pin);
          //
          // init A/D
          //
          ADMUX = (0 << REFS2) | (0 << REFS1) | (0 << REFS0) // Vcc ref
          | (0 << ADLAR) // right adjust
          | (0 << MUX3) | (0 << MUX2) | (1 << MUX1) | (0 << MUX0); // PB4
          ADCSRA = (1 << ADEN) // enable
          | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // prescaler /128
          //
          // main loop
          //
          char snum[5];

          while (1) {
              //
              // send framing
              //
              serOut("1");
              serOut("2");
              serOut("3");
              serOut("4");
              //
              // settle, charge, and wait 1
              //
              settle_delay();
              set(charge_port, charge_pin);
              charge_delay_1();
              collect_step(&up_lo, &up_hi, &down_lo, &down_hi, snum);


              //
              // settle, charge, and wait 2
              //
              settle_delay();
              set(charge_port, charge_pin);
              charge_delay_2();
              collect_step(&up_lo, &up_hi, &down_lo, &down_hi, snum);

              //
              // settle, charge, and wait 3
              //
              settle_delay();
              set(charge_port, charge_pin);
              charge_delay_3();
              collect_step(&up_lo, &up_hi, &down_lo, &down_hi, snum);

          }
      }

    

Using this method we can see that we won in app size and in some readability of code.

Assignments

Create real input device card

Now I have possibility to create a real card. My card will have 3 touch panels. It uses sensor based "step response" method. So touch is change the capacity assigned to the zone and it change the resistance that come with. So because of 1Mohm resistance - I can see the changes in voltage on pin. That I detect by checking values in high and in low state and comparing them.

Schema
PCB

Let make this card. First I did my visual check and I saw that in one location the trace was not cutted. So I need to fix it. And check it after again.

Cut
Visual check

Let solder components on it.

Cut
Visual check
Visual check

Here is the result. BTW the additional change was applied later (I added one wire to fix missed path on the schema - the VCC was not related to capacitor that comes to RST pin. So each time RST produced it provoked RST as well on my programmer side. So fixed)

Cut

Now let program it. I use the code provided by Neil to step response slightly modified. Also I used the .py file to recive data from one of 3 touch pads to visualize them. Below is the result and used code (C code)

So we can see how it works.


//
// hello.load.45.c
//
// step response loading hello-world
//    9600 baud FTDI interface
//
// Neil Gershenfeld
// 10/27/10
//
// (c) Massachusetts Institute of Technology 2010
// This work may be reproduced, modified, distributed,
// performed, and displayed for any purpose. Copyright is
// retained and must be preserved. The work is provided
// as is; no warranty is provided, and users accept all
// liability.
//

#include <avr/io.h>
#include <util/delay.h>

#define output(directions,pin) (directions |= pin) // set port direction for output
#define set(port,pin) (port |= pin) // set port pin
#define clear(port,pin) (port &= (~pin)) // clear port pin
#define pin_test(pins,pin) (pins & pin) // test for port pin
#define bit_test(byte,bit) (byte & (1 << bit)) // test for bit set
#define bit_delay_time 100 // bit delay for 9600 with overhead
#define bit_delay() _delay_us(bit_delay_time) // RS232 bit delay
#define half_bit_delay() _delay_us(bit_delay_time/2) // RS232 half bit delay
#define charge_delay_1() _delay_us(1) // charge delay 1
#define charge_delay_2() _delay_us(10) // charge delay 2
#define charge_delay_3() _delay_us(100) // charge delay 3
#define settle_delay() _delay_us(100) // settle delay
#define char_delay() _delay_ms(10) // char delay
#define ref (0 << REFS1) | (0 << REFS0) // reference voltage

#define serial_port PORTB
#define serial_direction DDRB
#define serial_pin_out (1 << PB1)
#define charge_port PORTA
#define charge_direction DDRA
#define charge_pin (1 << PA3)

void put_char(volatile unsigned char *port, unsigned char pin, char txchar) {
    //
    // send character in txchar on port pin
    //    assumes line driver (inverts bits)
    //
    // start bit
    //
    clear(*port,pin);
    bit_delay();
    //
    // unrolled loop to write data bits
    //
    if bit_test(txchar,0)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,1)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,2)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,3)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,4)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,5)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,6)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    if bit_test(txchar,7)
        set(*port,pin);
    else
        clear(*port,pin);
    bit_delay();
    //
    // stop bit
    //
    set(*port,pin);
    bit_delay();
    //
    // char delay
    //
    bit_delay();
}

int main(void) {
    //
    // main
    //
    static unsigned char up_lo,up_hi,down_lo,down_hi;
    //
    // set clock divider to /1
    //
    CLKPR = (1 << CLKPCE);
    CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
    //
    // initialize output pins
    //
    set(serial_port, serial_pin_out);
    output(serial_direction, serial_pin_out);
    clear(charge_port, charge_pin);
    output(charge_direction, charge_pin);
    //
    // init A/D
    //
    ADMUX = ref // Vcc ref
    | (0 << ADLAR) // right adjust
    | (0 << MUX2) | (0 << MUX1) | (0 << MUX0); // PB4
    ADCSRA = (1 << ADEN) // enable
    | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // prescaler /128
    //
    // main loop
    //
    while (1) {
        //
        // send framing
        //
        put_char(&serial_port, serial_pin_out, 1);
        char_delay();
        put_char(&serial_port, serial_pin_out, 2);
        char_delay();
        put_char(&serial_port, serial_pin_out, 3);
        char_delay();
        put_char(&serial_port, serial_pin_out, 4);
        //
        // settle, charge, and wait 1
        //
        settle_delay();
        set(charge_port, charge_pin);
        charge_delay_1();
        //
        // initiate conversion
        //
        ADCSRA |= (1 << ADSC);
        //
        // wait for completion
        //
        while (ADCSRA & (1 << ADSC))
            ;
        //
        // save result
        //
        up_lo = ADCL;
        up_hi = ADCH;
        //
        // settle, discharge, and wait 1
        //
        settle_delay();
        clear(charge_port, charge_pin);
        charge_delay_1();
        //
        // initiate conversion
        //
        ADCSRA |= (1 << ADSC);
        //
        // wait for completion
        //
        while (ADCSRA & (1 << ADSC))
            ;
        //
        // save result
        //
        down_lo = ADCL;
        down_hi = ADCH;
        //
        // send result
        //
        put_char(&serial_port, serial_pin_out, up_lo);
        char_delay();
        put_char(&serial_port, serial_pin_out, up_hi);
        char_delay();
        put_char(&serial_port, serial_pin_out, down_lo);
        char_delay();
        put_char(&serial_port, serial_pin_out, down_hi);
        char_delay();
        //
        // settle, charge, and wait 2
        //
        settle_delay();
        set(charge_port, charge_pin);
        charge_delay_2();
        //
        // initiate conversion
        //
        ADCSRA |= (1 << ADSC);
        //
        // wait for completion
        //
        while (ADCSRA & (1 << ADSC))
            ;
        //
        // save result
        //
        up_lo = ADCL;
        up_hi = ADCH;
        //
        // settle, discharge, and wait 2
        //
        settle_delay();
        clear(charge_port, charge_pin);
        charge_delay_2();
        //
        // initiate conversion
        //
        ADCSRA |= (1 << ADSC);
        //
        // wait for completion
        //
        while (ADCSRA & (1 << ADSC))
            ;
        //
        // save result
        //
        down_lo = ADCL;
        down_hi = ADCH;
        //
        // send result
        //
        put_char(&serial_port, serial_pin_out, up_lo);
        char_delay();
        put_char(&serial_port, serial_pin_out, up_hi);
        char_delay();
        put_char(&serial_port, serial_pin_out, down_lo);
        char_delay();
        put_char(&serial_port, serial_pin_out, down_hi);
        char_delay();
        //
        // settle, charge, and wait 3
        //
        settle_delay();
        set(charge_port, charge_pin);
        charge_delay_3();
        //
        // initiate conversion
        //
        ADCSRA |= (1 << ADSC);
        //
        // wait for completion
        //
        while (ADCSRA & (1 << ADSC))
            ;
        //
        // save result
        //
        up_lo = ADCL;
        up_hi = ADCH;
        //
        // settle, discharge, and wait 3
        //
        settle_delay();
        clear(charge_port, charge_pin);
        charge_delay_3();
        //
        // initiate conversion
        //
        ADCSRA |= (1 << ADSC);
        //
        // wait for completion
        //
        while (ADCSRA & (1 << ADSC))
            ;
        //
        // save result
        //
        down_lo = ADCL;
        down_hi = ADCH;
        //
        // send result
        //
        put_char(&serial_port, serial_pin_out, up_lo);
        char_delay();
        put_char(&serial_port, serial_pin_out, up_hi);
        char_delay();
        put_char(&serial_port, serial_pin_out, down_lo);
        char_delay();
        put_char(&serial_port, serial_pin_out, down_hi);
        char_delay();
    }
}