Part 19 - Input

The last two lessons hopefully showcased the need for a mature approach to handling input on any serious application.

Today we will design a proper input architecture for the Pico related to STDIN and STDIO.

Let's begin with creating an input.h as follows.

void input_proc(char type, char* p_usb_char, char* p_usb_string, const int* p_USB_STRING_SIZE);
void flush_input(char* p_usb_string);

Here we setup our input header file to address the params that we discussed in the last lesson. We also set up our flush_input function to handle clearing the input buffer after it is used to ensure it is clean before new input is obtained for another call to input_proc.

Next we will create our print.h as follows.

void print_proc(char* p_usb_char, char* p_usb_string);

Very simply we are going to pass in a char array from the caller to handle each char and a char array from the caller to handle the string creation.

Next we will create our input.c as follows.

#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"

#define ZERO 0x30
#define NINE 0x39
#define PERIOD 0x2e
#define CAPITAL_A 0x41
#define LOWER_CASE_Z 0x7a
#define BACKSPACE 0x08
#define DEL 0x7f

void input_proc(char type, char* p_usb_char, char* p_usb_string, const int* p_USB_STRING_SIZE)
{
  *p_usb_char = '\0';
  *p_usb_char = getchar_timeout_us(0);
  if(*p_usb_char == BACKSPACE || *p_usb_char == DEL)
  {
    if(p_usb_string[0] != '\0')
    {
      printf("\b");
      printf(" ");
      printf("\b");
      p_usb_string[strlen(p_usb_string)-1] = '\0';
    }
  }
  if(type == 'f')
  { 
    char* period;
    while((*p_usb_char >= ZERO && *p_usb_char <= NINE) || *p_usb_char == PERIOD)
    {
      if(*p_usb_char == PERIOD)
        period = strchr(p_usb_string, '.');
      if(period == NULL) 
      {
        if(strlen(p_usb_string) < *p_USB_STRING_SIZE)
        {
          putchar(*p_usb_char);
          strncat(p_usb_string, p_usb_char, 1);
        }
        *p_usb_char = '\0';
      }
      else
        break;
    }
  }
  else if(type == 'd')
  { 
    while(*p_usb_char >= ZERO && *p_usb_char <= NINE)
    {
      if(strlen(p_usb_string) < *p_USB_STRING_SIZE)
      {
        putchar(*p_usb_char);
        strncat(p_usb_string, p_usb_char, 1);
      }
      *p_usb_char = '\0';
    }
  }
  else if(type == 's')
  { 
    while(*p_usb_char >= CAPITAL_A && *p_usb_char <= LOWER_CASE_Z)
    {
      if(strlen(p_usb_string) < *p_USB_STRING_SIZE)
      {
        putchar(*p_usb_char);
        strncat(p_usb_string, p_usb_char, 1);
      }
      *p_usb_char = '\0';
    }
  }
}

void flush_input(char* p_usb_string)
{
  p_usb_string[0] = '\0';
}

Everything should be fully understood at this point with the above. If it is not please review the last two lessons.

Next we will create our print.c as follows.

#include <stdio.h>
#include "pico/stdlib.h"
#include "input.h"

#define RETURN 0x0d

void print_proc(char* p_usb_char, char* p_usb_string)
{
  if(*p_usb_char == RETURN)
  {
    if(p_usb_string[0] == '\0')
      printf("\n");
    else
      printf("\n%s\n", p_usb_string);
    flush_input(p_usb_string);
  }
}

Here we bring in our char and string capability and if the return key is pressed will print the contents of the string and then call the flush_input to clear the buffer as discussed.

Finally we will create our main.c as follows.

#include <stdio.h>
#include "pico/stdlib.h"
#include "print.h"
#include "input.h"

int main()
{
  stdio_init_all();

  const int USB_STRING_SIZE = 100;
  char usb_char;
  usb_char = '\0';
  char usb_string[USB_STRING_SIZE];
  usb_string[0] = '\0';
  
  while(1)
  {   
    input_proc('f', &usb_char, usb_string, &USB_STRING_SIZE);
    print_proc(&usb_char, usb_string);
  }

  return 0;
}

Here we simply set up our input procedure to handle float input.

Let's make a new dir 0x07_input and add our CMakeLists.txt file in it.

cmake_minimum_required(VERSION 3.13)

include(pico_sdk_import.cmake)

project(test_project C CXX ASM)
set(CMAKE_C_STANDARD 11) 
set(CMAKE_CXX_STANDARD 17) 
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
pico_sdk_init()

add_executable(main
  main.c
  print.c
  input.c
)

pico_enable_stdio_usb(main 1)
pico_enable_stdio_uart(main 0)
pico_add_extra_outputs(main)

target_link_libraries(main pico_stdlib hardware_i2c)

add_custom_target(flash
    COMMAND cp main.uf2 /Volumes/RPI-RP2/
    DEPENDS main
)

Next we need to copy the pico_sdk_import.cmake file from the external folder in the pico-sdk installation to the 0x07_input project folder.

cp ../pico-sdk/external/pico_sdk_import.cmake .

Finally we are ready to build.

mkdir build
cd build
export PICO_SDK_PATH=../../pico-sdk
cmake ..
make
make flash

I added a flash routine in the makefile to save us time from copying to the Pico. Remember to put the Pico into flash mode first.

Then we need to locate the USB drive so you can do the following.

ls /dev/tty.

Press tab to find the drive and then in my case I will use screen to connect.

screen /dev/tty.usbmodem0000000000001

Boom! Now you will see you will ONLY be able to enter in numbers and ONLY ONE decimal point. We properly handle for backspacing and when you reach the max of 100 chars it will not allow you to type further. Finally it prints back what you typed.

32.3333
32.3333
32.11111111
32.11111111
7.99999003902930420384802384082304820384028342340284923840238948230482938429034823948293849023849223
7.99999003902930420384802384082304820384028342340284923840238948230482938429034823948293849023849223

In our next lesson we will debug.

results matching ""

    No results matching ""