first commit
This commit is contained in:
commit
a5a0434432
1126 changed files with 439481 additions and 0 deletions
32
Software/Rust/README.md
Normal file
32
Software/Rust/README.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Rust support for GrovePi
|
||||
|
||||
## Introduction
|
||||
Rust Crate support for using GrovePi with Rust.
|
||||
|
||||
Examples can be found in the relevant directories as `main.rs`.
|
||||
Check them out for usage.
|
||||
|
||||
## Dependencies
|
||||
* Rust version above 1.65 is known to work.
|
||||
* The Rust crate [**rppal**](https://github.com/golemparts/rppal) is required and is referenced in the `Cargo.toml` file.
|
||||
|
||||
## Current State
|
||||
As the initial state of this module the following features are implemented and tested:
|
||||
|
||||
* Read/write data to I2C slave device [**Grove-LCD RGB Backlight**](https://wiki.seeedstudio.com/Grove-LCD_RGB_Backlight/) V. 4.0 present on ports I2C-1, I2C-2 or I2C-3.
|
||||
* Tested on Raspberry Pi 3, Model B and Raspberry Pi, Model B, Rev 2
|
||||
with Raspbian version: September 2022
|
||||
|
||||
## Cross compile for Rasbperry Pi
|
||||
|
||||
Follow the setup instructions from - (https://github.com/cross-rs/cross)
|
||||
|
||||
Then `cross build --target arm-unknown-linux-gnueabi`. A pi-ready
|
||||
and transfer the binary (`target/arm-unknown-linux-gnueabi/debug/airq`) to the Pi.
|
||||
|
||||
Run the binary `grove_rgb_lcd` on the Pi and observe a couple debug messages and changing colors.
|
||||
|
||||
## Add the module `grove_rgb_lcd` to your projects
|
||||
|
||||
Be sure to add the `rppal` dependency to your `Cargo.toml`
|
||||
|
||||
2
Software/Rust/grove_rgb/.cargo/config
Normal file
2
Software/Rust/grove_rgb/.cargo/config
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[target.armv7-unknown-linux-gnueabi]
|
||||
linker = "arm-linux-gnueabi-gcc"
|
||||
27
Software/Rust/grove_rgb/.vscode/launch.json
vendored
Normal file
27
Software/Rust/grove_rgb/.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug executable 'grove_rgb'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=grove_rgb",
|
||||
"--package=grove_rgb"
|
||||
],
|
||||
"filter": {
|
||||
"name": "grove_rgb",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"program": "${workspaceFolder}/target/debug/grove_rgb",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
74
Software/Rust/grove_rgb/Cargo.lock
generated
Normal file
74
Software/Rust/grove_rgb/Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[package]]
|
||||
name = "grove_rgb"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"regex",
|
||||
"rppal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
|
||||
[[package]]
|
||||
name = "rppal"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "612e1a22e21f08a246657c6433fe52b773ae43d07c9ef88ccfc433cc8683caba"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
9
Software/Rust/grove_rgb/Cargo.toml
Normal file
9
Software/Rust/grove_rgb/Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "grove_rgb"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rppal = "0"
|
||||
184
Software/Rust/grove_rgb/src/grove_rgb_lcd.rs
Normal file
184
Software/Rust/grove_rgb/src/grove_rgb_lcd.rs
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
use std::io;
|
||||
use std::{thread, time};
|
||||
|
||||
use rppal::i2c;
|
||||
|
||||
|
||||
// device structure for Grove RGB LCD Display
|
||||
#[derive(Debug)]
|
||||
pub struct GroveRgbLcd
|
||||
{
|
||||
i2c: i2c::I2c,
|
||||
// row_cursor keeps track of NEXT ROW to be written -- internal state
|
||||
// logic in #write_row will clear/reset the display when next row is 0
|
||||
// the #write_line method will advance to the next row on column wrap
|
||||
// -OR- on newlines in the input text
|
||||
//
|
||||
// This means that long inputs will just overwrite and may not be seen
|
||||
// if this behavior is NOT desired, manage input to the LINE_LENGTH
|
||||
// and add delays, etc.
|
||||
row_cursor: u8,
|
||||
}
|
||||
|
||||
|
||||
pub const DISPLAY_RGB_ADDR: u16 = 0x62;
|
||||
pub const DISPLAY_TEXT_ADDR: u16 = 0x3e;
|
||||
|
||||
const PROGRAM_MODE: u8 = 0x80;
|
||||
|
||||
const CLEAR_DISPLAY: u8 = 0x01;
|
||||
const DISPLAY_ON: u8 = 0x08;
|
||||
const NO_CURSOR: u8 = 0x04;
|
||||
const ENABLE_2ROWS: u8 = 0x28;
|
||||
|
||||
const LINE_LENGTH: usize = 16;
|
||||
|
||||
pub const DISPLAY_CHAR: u8 = 0x40;
|
||||
pub const NEW_ROW: u8 = 0xc0;
|
||||
|
||||
|
||||
impl GroveRgbLcd
|
||||
{
|
||||
pub fn set_text(&mut self, text: &str) -> Result<(), io::Error>
|
||||
{
|
||||
match self.impl_set_text(text)
|
||||
{
|
||||
Err(err) => Err(io::Error::new(io::ErrorKind::Other, err)),
|
||||
Ok(_) => Ok(())
|
||||
}
|
||||
}
|
||||
pub fn set_rgb(&mut self, (r, g, b): (u8, u8, u8)) -> Result<(), io::Error>
|
||||
{
|
||||
match self.impl_set_rgb((r, g, b))
|
||||
{
|
||||
Err(err) => Err(io::Error::new(io::ErrorKind::Other, err)),
|
||||
Ok(_) => Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GroveRgbLcd
|
||||
{
|
||||
fn select_slave(&mut self, addr: u16) -> i2c::Result<()>
|
||||
{
|
||||
println!("set slave {:x}", addr );
|
||||
self.i2c.set_slave_address(addr)
|
||||
}
|
||||
|
||||
fn clear_display(&mut self) -> i2c::Result<()>
|
||||
{
|
||||
println!("set display");
|
||||
self.i2c.block_write(PROGRAM_MODE, &[CLEAR_DISPLAY])?;
|
||||
thread::sleep(time::Duration::from_millis(50));
|
||||
|
||||
self.row_cursor = 0;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_on_no_cursor(&mut self) -> i2c::Result<()>
|
||||
{
|
||||
self.i2c.block_write(PROGRAM_MODE, &[DISPLAY_ON | NO_CURSOR])
|
||||
}
|
||||
|
||||
fn enable_2rows(&mut self) -> i2c::Result<()>
|
||||
{
|
||||
self.i2c.block_write(PROGRAM_MODE, &[ENABLE_2ROWS])?;
|
||||
thread::sleep(time::Duration::from_millis(50));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new_row(&mut self) -> i2c::Result<()>
|
||||
{
|
||||
self.i2c.block_write(PROGRAM_MODE, &[NEW_ROW])?;
|
||||
self.row_cursor = (self.row_cursor + 1)%2;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// writes a row of chars -- no checking for line length or newline
|
||||
fn write_row(&mut self, row: &[u8]) -> i2c::Result<()>
|
||||
{
|
||||
if self.row_cursor == 0
|
||||
{
|
||||
self.clear_display()?;
|
||||
}
|
||||
|
||||
for ch in row
|
||||
{
|
||||
self.i2c.block_write(DISPLAY_CHAR, &[*ch])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// write_line()
|
||||
// a line has no '\n's in it... but will be wrapped at the display length
|
||||
// and written on two rows
|
||||
// Display is cleared before writing the line
|
||||
fn write_line(&mut self, line: &str) -> i2c::Result<()>
|
||||
{
|
||||
println!("writing line: {}", line) ;
|
||||
|
||||
for row in line.as_bytes().chunks(LINE_LENGTH)
|
||||
{
|
||||
self.write_row(row)?;
|
||||
self.new_row()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn impl_set_text(&mut self, text: &str) -> Result<(), i2c::Error>
|
||||
{
|
||||
self.select_slave(DISPLAY_TEXT_ADDR)?;
|
||||
|
||||
self.clear_display()?;
|
||||
|
||||
self.display_on_no_cursor()?;
|
||||
self.enable_2rows()?;
|
||||
|
||||
// spool out text one char at a time
|
||||
// First split on newlines (user formatting) with .lines()
|
||||
// then split those lines a line length (wrapped lines) with .chunks()
|
||||
|
||||
println!("ready to write {}", text);
|
||||
|
||||
for line in text.lines()
|
||||
{
|
||||
self.write_line(line)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn impl_set_rgb(&mut self, (r, g, b): (u8, u8, u8)) -> Result<(), i2c::Error>
|
||||
{
|
||||
self.select_slave(DISPLAY_RGB_ADDR)?;
|
||||
|
||||
self.i2c.block_write(0x00, &[0x00])?;
|
||||
self.i2c.block_write(0x01, &[0x00])?;
|
||||
self.i2c.block_write(0x08, &[0xAA])?;
|
||||
|
||||
|
||||
self.i2c.block_write(0x04, &[r])?;
|
||||
self.i2c.block_write(0x03, &[g])?;
|
||||
self.i2c.block_write(0x02, &[b])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect() -> Result<GroveRgbLcd, io::Error>
|
||||
{
|
||||
println!("connecting I2C bus");
|
||||
|
||||
match i2c::I2c::new()
|
||||
{
|
||||
Err(err) => Err(io::Error::new(io::ErrorKind::Other, err)),
|
||||
Ok(i2c) => Ok(GroveRgbLcd{i2c: i2c, row_cursor: 0}),
|
||||
}
|
||||
}
|
||||
|
||||
26
Software/Rust/grove_rgb/src/main.rs
Normal file
26
Software/Rust/grove_rgb/src/main.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
use std::error::Error;
|
||||
use std::{thread, time};
|
||||
|
||||
mod grove_rgb_lcd;
|
||||
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
println!("Hello, LCD");
|
||||
|
||||
let mut display: grove_rgb_lcd::GroveRgbLcd = grove_rgb_lcd::connect()?;
|
||||
println!("{:?}", display);
|
||||
|
||||
display.set_rgb((0x80, 0x00, 0x00))?; // red
|
||||
display.set_text("FUBAR!\n@@snafu\nline1\nline2\n0123456789ABCDEF0123456789abcdef")?;
|
||||
thread::sleep(time::Duration::from_millis(5000));
|
||||
|
||||
display.set_rgb((0x20, 0xA0, 0x40))?;
|
||||
display.set_text("dunno what color this is")?;
|
||||
thread::sleep(time::Duration::from_millis(5000));
|
||||
|
||||
display.set_rgb((0x00, 0x40, 0xFF))?;
|
||||
display.set_text("now i'm bright BLUE - like my ballz!")?;
|
||||
thread::sleep(time::Duration::from_millis(5000));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue