diff --git a/README.md b/README.md index 01689e58d..5aee72c04 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ App|Description [lcd_1602_i2c](i2c/lcd_1602_i2c) | Display some text on a generic 16x2 character LCD display, via I2C. [mpl3115a2_i2c](i2c/mpl3115a2_i2c) | Interface with an MPL3115A2 altimeter, exploring interrupts and advanced board features, via I2C. [mpu6050_i2c](i2c/mpu6050_i2c) | Read acceleration and angular rate values from a MPU6050 accelerometer/gyro, attached to an I2C bus. +[pa1010d_i2c](i2c/pa1010d_i2c) | Read GPS location data, parse and display data via I2C. [oled_i2c](i2c/oled_i2c) | Convert and display a bitmap on a 128x32 SSD1306-driven OLED display ### Interpolator diff --git a/i2c/CMakeLists.txt b/i2c/CMakeLists.txt index e7d370397..53b8d7e26 100644 --- a/i2c/CMakeLists.txt +++ b/i2c/CMakeLists.txt @@ -4,5 +4,6 @@ if (NOT PICO_NO_HARDWARE) add_subdirectory(lcd_1602_i2c) add_subdirectory(mpl3115a2_i2c) add_subdirectory(mpu6050_i2c) + add_subdirectory(pa1010d_i2c) add_subdirectory(oled_i2c) endif () diff --git a/i2c/pa1010d_i2c/CMakeLists.txt b/i2c/pa1010d_i2c/CMakeLists.txt new file mode 100644 index 000000000..b04fac23f --- /dev/null +++ b/i2c/pa1010d_i2c/CMakeLists.txt @@ -0,0 +1,12 @@ +add_executable(pa1010d_i2c + pa1010d_i2c.c + ) + +# pull in common dependencies and additional i2c hardware support +target_link_libraries(pa1010d_i2c pico_stdlib hardware_i2c) + +# create map/bin/hex file etc. +pico_add_extra_outputs(pa1010d_i2c) + +# add url via pico_set_program_url +example_auto_set_url(pa1010d_i2c) diff --git a/i2c/pa1010d_i2c/README.adoc b/i2c/pa1010d_i2c/README.adoc new file mode 100644 index 000000000..48877c433 --- /dev/null +++ b/i2c/pa1010d_i2c/README.adoc @@ -0,0 +1,42 @@ += Attaching a PA1010D Mini GPS module via I2C + +This example code shows how to interface the Raspberry Pi Pico to the PA1010D Mini GPS module +====== +This allows you read basic location and time data from the Recommended Minimum Specific GNSS Sentence (GNRMC protocol) and displays it in a user-friendly format. The datasheet for the module can be found on https://cdn-learn.adafruit.com/assets/assets/000/084/295/original/CD_PA1010D_Datasheet_v.03.pdf?1573833002. The output sentence is read and parsed to split the data fields into a 2D character array, which are then individually printed out. The commands to use different protocols and change settings are found on https://www.sparkfun.com/datasheets/GPS/Modules/PMTK_Protocol.pdf. Additional protocols can be used by editing the init_command array. +====== +[NOTE] +====== +Each command requires a checksum after the asterisk. The checksum can be calculated for your command using the following website: https://nmeachecksum.eqth.net/. + +The GPS needs to be used outdoors in open skies and requires about 15 seconds to acquire a satellite signal in order to display valid data. When the signal is detected, the device will blink a green LED at 1 Hz. +====== + + +== Wiring information + +Wiring up the device requires 4 jumpers, to connect VDD, GND, SDA and SCL. The example here uses I2C port 0, which is assigned to GPIO 4 (SDA) and 5 (SCL) in software. Power is supplied from the 3V pin. + + +[[pa1010d_i2c_wiring]] +[pdfwidth=75%] +.Wiring Diagram for PA1010D. +image::pa1010d_i2c.png[] + +== List of Files + +CMakeLists.txt:: CMake file to incorporate the example in to the examples build tree. +pa1010d_i2c.c:: The example code. + +== Bill of Materials + +.A list of materials required for the example +[[pa1010d-bom-table]] +[cols=3] +|=== +| *Item* | *Quantity* | Details +| Breadboard | 1 | generic part +| Raspberry Pi Pico | 1 | http://raspberrypi.org/ +| PA1010D board| 1 | https://shop.pimoroni.com/products/pa1010d-gps-breakout +| M/M Jumper wires | 4 | generic part +|=== + diff --git a/i2c/pa1010d_i2c/pa1010d_i2c.c b/i2c/pa1010d_i2c/pa1010d_i2c.c new file mode 100644 index 000000000..7e895e285 --- /dev/null +++ b/i2c/pa1010d_i2c/pa1010d_i2c.c @@ -0,0 +1,153 @@ +/** + * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "pico/stdlib.h" +#include "pico/binary_info.h" +#include "hardware/i2c.h" +#include "string.h" + +/* Example code to talk to a PA1010D Mini GPS module. + + This example reads the Recommended Minimum Specific GNSS Sentence, which includes basic location and time data, each second, formats and displays it. + + Connections on Raspberry Pi Pico board, other boards may vary. + + GPIO PICO_DEFAULT_I2C_SDA_PIN (On Pico this is 4 (physical pin 6)) -> SDA on PA1010D board + GPIO PICO_DEFAULT_I2C_SCK_PIN (On Pico this is 5 (physical pin 7)) -> SCL on PA1010D board + 3.3v (physical pin 36) -> VCC on PA1010D board + GND (physical pin 38) -> GND on PA1010D board +*/ + +const int addr = 0x10; +const int max_read = 250; + +#ifdef i2c_default +void pa1010d_write_command(char command[], int com_length){ + // Convert character array to bytes for writing + uint8_t int_command[com_length]; + + for (int i = 0; i < com_length; ++i){ + int_command[i] = command[i]; + i2c_write_blocking(i2c_default, addr, &int_command[i], 1, true); + } +} + +void pa1010d_parse_string(char output[], char protocol[]){ + // Finds location of protocol message in output + char *com_index = strstr(output,protocol); + int p = com_index - output; + + // Splits components of output sentence into array + int no_of_fields = 14; + int max_len = 15; + + int n = 0; + int m = 0; + + char gps_data[no_of_fields][max_len]; + memset(gps_data, 0, sizeof(gps_data)); + + bool complete = false; + while (output[p] != '$' && n < max_len && complete == false){ + if (output[p] == ','|| output[p] == '*'){ + n += 1; + m = 0; + } + else{ + gps_data[n][m] = output[p]; + // Checks if sentence is complete + if (m < no_of_fields){ + m++; + } else { + complete = true; + } + } + p++; + } + + // Displays GNRMC data + // Similarly, additional if statements can be used to add more protocols + if (strcmp(protocol, "GNRMC") == 0){ + printf("Protcol:%s\n", gps_data[0]); + printf("UTC Time: %s\n", gps_data[1]); + printf("Status: %s\n", gps_data[2][0] == 'V'? "Data invalid. GPS fix not found.":"Data Valid"); + printf("Latitude: %s\n", gps_data[3]); + printf("N/S indicator: %s\n", gps_data[4]); + printf("Longitude: %s\n", gps_data[5]); + printf("E/W indicator: %s\n", gps_data[6]); + printf("Speed over ground: %s\n", gps_data[7]); + printf("Course over ground: %s\n", gps_data[8]); + printf("Date: %c%c/%c%c/%c%c\n", gps_data[9][0], gps_data[9][1], gps_data[9][2], gps_data[9][3], gps_data[9][4], gps_data[9][5]); + printf("Magnetic Variation: %s\n", gps_data[10]); + printf("E/W degree indicator: %s\n", gps_data[11]); + printf("Mode: %s\n", gps_data[12]); + printf("Checksum: %c%c\n", gps_data[13][0], gps_data[13][1]); + } +} + +void pa1010d_read_raw(char numcommand[]) { + uint8_t buffer[max_read]; + + int i = 0; + bool complete = false; + + i2c_read_blocking(i2c_default, addr, buffer, max_read, false); + + // Convert bytes to characters + while(i < max_read && complete == false){ + numcommand[i] = buffer[i]; + // Stop converting at end of message + if (buffer[i] == 10 && buffer[i + 1] == 10){ + complete = true; + } + i++; + } +} +#endif + +int main() { + stdio_init_all(); +#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) + #warning i2c/mpu6050_i2c example requires a board with I2C pins + puts("Default I2C pins were not defined"); +#else + + char numcommand[max_read]; + + // Decide which protocols you would like to retrieve data from + char init_command[] = "$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29\r\n"; + + // This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico) + i2c_init(i2c_default, 400 * 1000); + gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C); + gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C); + gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN); + gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN); + // Make the I2C pins available to picotool + bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); + + printf("Hello, PA1010D! Reading raw data from module...\n"); + + pa1010d_write_command(init_command, sizeof(init_command)); + + while (1) { + // Clear array + memset(numcommand, 0, max_read); + // Read and re-format + pa1010d_read_raw(numcommand); + pa1010d_parse_string(numcommand, "GNRMC"); + + // Wait for data to refresh + sleep_ms(1000); + + // Clear terminal + printf("\e[1;1H\e[2J"); + } +#endif + return 0; +} diff --git a/i2c/pa1010d_i2c/pa1010d_i2c.fzz b/i2c/pa1010d_i2c/pa1010d_i2c.fzz new file mode 100644 index 000000000..94816035e Binary files /dev/null and b/i2c/pa1010d_i2c/pa1010d_i2c.fzz differ diff --git a/i2c/pa1010d_i2c/pa1010d_i2c.png b/i2c/pa1010d_i2c/pa1010d_i2c.png new file mode 100644 index 000000000..b04de724a Binary files /dev/null and b/i2c/pa1010d_i2c/pa1010d_i2c.png differ