#Arduino Help
1 messages · Page 3 of 1
It just decides to jump 2, weird
When you go out of scope in characters
It does weird things
So just
Live with that limitation
If you need to write
A long sentence
yep, thats what your struct will fix 
Just divide it in two lines with two calls to write
alright so it works
I just wanted to make sure that the second and forth line were working
Ok so I guess we can do the first part which is like my original code, just make it work write the text we need and so all manual, and when it works then make a function and stuff to make it like the last version
I just realized something..
I am thinking something like:
struct Lcd {
lcd: Display,
}
impl Lcd {
pub fn show_list(&mut self, list: &[&str]) {
...
}
}
That could be problematic
Well idk if it will
But we kinda need to use the serial port
Do you want to set that up?
To emulate using the keyboard
Because if not
We kinda
Can't test it
We can do something simple
alright
back to the docs!
I only cared that it stayed until I sensed something
I didn't care what
Tho..
Back then
okay do you know how the Arduino connects to the computer to send text? 
I only did that because I thought the LCD wouldn't care for what I pressed which I quickly realized it wasn't the case
I know how you use the serial porrt in the arduino IDE
But here
Like with the I2c we need to find the device to talk to
I could show you the code for the LCD very first shitty version
Which used the serial port only to wait for something to be send
Read it, do nothing and wait again
Let me see..
The hint is: Usart is the name of the device
Ok this shit doesn't let me copy the text
But there you can see it
It basically was
Serial.available that will only enter when there was something to read, but then I couldn't use it again without reading to it because next time I used serial.available there sould be something
Tho I think I made a better version of it if I remember
I made a function waitForInput
But it's not there
Tho idk if showing this will help because I have no clue how are we using the serial port here
It can be use in vs code because you can kinda have the arduino IDE here
But
That would be arduino IDE and we don't program there
There's ofc another solution
And it's to keep working on the LCD without it
And just comment a previous text that worked and see if we can write another
Then leave it where we can and when we do keyboard
I think we should set it up. It's really easy
Properly test it
But I think using the serial port it's inevitable
Because of..
The RFID
It's impossible to test otherwise so..
first task, find the docs for it 
How do we do that?
Oh btw I forgot to mention but I have kinda good news
The keyboard
I had done if I recall
cargo doc --open
A code in arduino without a library that could detect the key I pressed so
It's likely going to be way more simple even if there wasn't a library for it, but if there is, piece of bread
The one from before? I haven't close it
yep
scroll to the top, click the header to go back to the root of the docs
arduino_hal is the one we want to start with
yep
Go to the Usart one
let dp = arduino_uno::Peripherals::take().unwrap();
let mut pins = arduino_uno::Pins::new(dp.PORTB, dp.PORTC, dp.PORTD);
let mut serial = arduino_uno::Serial::new(
dp.USART0,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600.into_baudrate(),
);
ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").void_unwrap();
loop {
let b = nb::block!(serial.read()).void_unwrap();
ufmt::uwriteln!(&mut serial, "Got {}!\r", b).void_unwrap();
}
arduino_uno::Serial::new -> arduino_hal::Usart::new
The dp and pins you already have also
add ```rust
let mut serial = arduino_hal::Usart::new(
dp.USART0,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600.into_baudrate(),
);
Below dp?
yep
Maybe just maybe write_byte would do
Or in any case
Read and write
So we could see what we send and respond
The example shows how to use them
Oh then just read
the into_output() doesn't need the &mut borrow
And we store that data we read and maybe match it or use and if block
The example is a little old and the API changed
And the baudrate you can do as arduino_hal::usart::Baudrate::new(57600)
or mark it as a u32 with 57600u32.into_baudrate()
Ok no errors
I used this one
So what now?
now add a ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").void_unwrap();
Below serial?
yep
Checking rust_lcd v0.1.0 (C:\Users\fuchi\OneDrive\Documentos\Codigos_Rust\rust_lcd)
error[E0599]: no method named `void_unwrap` found for enum `Result` in the current scope
--> src\main.rs:22:59
|
22 | ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").void_unwrap();
| ^^^^^^^^^^^
|
::: C:\Users\fuchi\.cargo\registry\src\github.com-1ecc6299db9ec823\void-1.0.2\src\lib.rs:87:8
|
87 | fn void_unwrap(self) -> T;
| ----------- the method is available for `Result<(), void::Void>` here
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
3 | use arduino_hal::prelude::*; // trait ResultVoidExt
|
help: there is an associated function with a similar name
|
22 | ufmt::uwriteln!(&mut serial, "Hello from Arduino!\r").unwrap();
| ~~~~~~
For more information about this error, try `rustc --explain E0599`.
error: could not compile `rust_lcd` due to previous error
add use arduino_hal::prelude::*;
as the help says 
In all rust projects I always see 9000 use statements
yep, depends how you structure things
basically each use is an external thing this code needs
I mean it executed but..
Since there's also the LCD stuff let me see if it got lost in the terminal
No, nothing happened
Did you do a cargo run ?
Maybe because we didn't read what we write?
Yes
Compiling rust_lcd v0.1.0 (C:\Users\fuchi\OneDrive\Documentos\Codigos_Rust\rust_lcd)
Finished dev [optimized + debuginfo] target(s) in 4.26s
Running `ravedude mega2560 -b 57600 target\avr-atmega2560\debug\rust_lcd.elf --port COM6`
Board Arduino Mega 2560
Programming target\avr-atmega2560\debug\rust_lcd.elf => COM6
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.03s
avrdude: Device signature = 0x1e9801 (probably m2560)
avrdude: reading input file "target\avr-atmega2560\debug\rust_lcd.elf"
avrdude: writing flash (5994 bytes):
Writing | ################################################## | 100% 1.28s
avrdude: 5994 bytes of flash written
avrdude: verifying flash memory against target\avr-atmega2560\debug\rust_lcd.elf:
avrdude: load data flash data from input file target\avr-atmega2560\debug\rust_lcd.elf:
avrdude: input file target\avr-atmega2560\debug\rust_lcd.elf contains 5994 bytes
avrdude: reading on-chip flash data:
Reading | ################################################## | 100% 1.15s
avrdude: verifying ...
avrdude: 5994 bytes of flash verified
avrdude: safemode: Fuses OK (E:FF, H:D8, L:FF)
avrdude done. Thank you.
Programmed target\avr-atmega2560\debug\rust_lcd.elf
PS C:\Users\fuchi\OneDrive\Documentos\Codigos_Rust\rust_lcd>
This is the output
you removed the -c from ravedude 
in the .cargo/config.toml
Ah right
But you can just do cargo run -- --port COM6 -c to add it back
Ok now yes
Tho what we need is to read our input like the keyboard and depending on that what the LCD will show next
nb::block!(serial.read()).void_unwrap()
will wait for one Enter key
Let's see, let's put that above the write
If that returns the one entered key as an ASCIJ
It's perfect
yep it will return a single ascii byte
Because if you say
Saw*
In my code both versions
I merely use
WaitforKey
Which does just thatb
Stops everything waiting for a key
And gives it back
And then I ask in an if which key it was
So we have what we need
Btw new error
Tho just like write
I'll had to add another use
use embedded_hal::prelude::_embedded_hal_serial_Read;
I do love that cargo just tells you what to do 
yep, you should have everything to rebuild that first version
Next will be
To understand the basics on how to I mean
I will ask you about all that shit in the module later
For now
I just want to understand the how to use
Basically
which part?
let i2c = arduino_hal::I2c::new(
dp.TWI,
pins.d20.into_pull_up_input(),
pins.d21.into_pull_up_input(),
50000,
);
let lcd_conn = LcdConnection::new(i2c);
let mut lcd = Display::new(lcd_conn);
lcd.init(FunctionLine::Line2, FunctionDots::Dots5x8);
lcd.display(
DisplayMode::DisplayOn,
DisplayCursor::CursorOff,
DisplayBlink::BlinkOff);
lcd.entry_mode(EntryModeDirection::EntryRight, EntryModeShift::NoShift);
lcd.position(0, 1);
write!(&mut lcd, "Hello, my number today is {: >4}", 42).unwrap();
That
And I think
We would end for today
It's gonna be 5 am
Tomorrow I'm absent from 15:00 to 18:00 Argentine hour tho
let i2c = arduino_hal::I2c::new(
dp.TWI,
pins.d20.into_pull_up_input(),
pins.d21.into_pull_up_input(),
50000,
);
I think there's like two hours less
Wait for me to put everything away
And then we'll talk about it
Ok I'm back
So first we create a variable called i2c, what does it stored and why doesn't it need to be mutable?
This creates a I2c device on pins d20 and d21. The TWI is the i2c port we are using which there is just one on the mega, the 50000, is the speed of the communication
Store*
It will manage d20 and d21 to perform i2c communication
I2c device?
The mega has some special hardware internally that can connect to pins d20 and d21 that knows how to talk to i2c devices
i2c device is the micro component it talks to in this case the LCD?
Its like
Mega -> mega i2c controller -> lcd i2c controller -> LCD
the lcd i2c controller is the extra board attached to the back of the LCD
And which is the device, the controller?
the variable i2c represents the mega i2c controller
Ok
So we create a variable called i2c that is equal to a new instance of an I2c device which takes as parameters to it's "constructor" The I2c port , the pins that communicate one with the other and the speed of communication
Right?
yep
The speed is in bits per second? Is there are reason to pick that number?
The speed is in Hz of the i2c clock which is sort of bits per second
Oh ok
50000 is a standard supported speed. the mega can go faster but not all devices can
And then we create a new instance of our struct LcdConnection that represented the Lcd connections wow and pass in as a parameter the device
yep, we layer a LcdConnection on top of the i2c
I don't remember how was the struct inside let me scroll up but in the meantime
Why do we pass in the device as an argument?
Because the LcdConnection needs it to do it's job (sending instructions to the LCD controller) and we can only make a I2c from the dp and pins which main.rs owns
Ok then next is the lcd variable
I see another structure come into play another layer
yep, this layer's job is to know what instructions to send when
the Display?
So the others make them between the two and he sends them?
yep, the I2c knows nothing about LCDs it just sends data. The LcdConnection doesn't know what commands the LCD expects it just knows how to send them over I2c. The Display doesn't know about I2c but does know the commands to send
And then finally having the mutable variable lcd that stores all that setup we can start calling methods obviously starting with init but, what the heck are init's argumenta
In arduino init just turns it on
But here it receives arguments
This type of LCD has some options you can configure
The two in init are the line mode (which seems to be underlined or not) and the size of each character in dots
Some LCD of this type has 5x8 dot characters some have 5x10 dots
you will probably get weird things on screen if you do
just like with the Line1 
Ok then there's display tho it's just setting enums like configuration to ask if you want to show a cursor and it's blinking this is going to be really useful for me, but what about display mode haven't we turned it on before?
you can turn off the display, but save the text that was on it
Useful in some devices
So it's the way you would turn off the display
yep
And, what is entry_mode?
I think thats how it places text if you just write to it
Wdym by if you just write into it?
I think EntryRight prints left to right
I think I get the entry mode direction I guess it's if it's starts writing from left to right or vice versa
Oh ok
Like normal
and shift likely does the wrapping
It's a lot more customizable
Wrapping?
It either makes it go to a new line (properly) or it scrolls the text to the left if you have to much
I am not exactly sure
Mmm we will have to test it tomorrow
It says no shift
We will leave like that for now
there is also a Scroll option you can set
Well I guess that would be all the setup so now we can actually write by saying position first , this is just like setCursor in arduino, where you start from rows and colums
What would scroll do?
I think it scrolls the display vertically
Oh so for like a lot of text?
In a loop ig
It calls my attention there's no lcd print or write but it's rather a macro.
I am not sure, I haven't used a LCD like these. I have only used a different driver
the macro mimics print!() but writes to the screen
So, it takes a mutable reference to the variable, and the other argument it's the text, and it could fail for whatever reason idk, so we call unwrap to deal with it's returning result type with panic if it's an error, right?
there is a lcd.write_char('a'); if you want to put one char
.
And my last question about the write macro
yep, the ufmt library allows the thing to error, but here the usart cant
If that's ok, would be the weird syntax of {: >4}, 42 , inside the bracket goes the 42 but instead of just writting the 42 you write a weird thing inside brackets
?eval println!("{:>4}", 42);
42
()
It right aligns with 4 chars
I think that would be necessary with lcd.position tho
but this way you don't have to do the math 
?eval println!("{:>20}", "hello");
hello
()
This would right align on your display (4x20)
Well now that I understand all the setup needed to be able to write to it, and how to write to it and set the position of start, as well as how to make the cursor appear or disappear, blink, stop blinking, I'm kissing one thing
If there's a clear method which should be to erase the previous text to write a new one
ah, you can call lcd.clear(); https://docs.rs/lcd/latest/lcd/struct.Display.html#method.clear
Object implementing HD44780 protocol. Stateless (could be created as many times as needed).
And with that I think I have the equivalents to all the instructions I used and know for the C++ equivalent
Well my plan is the following
Tomorrow my goals are two
?eval println!("{:^20}", "hello");
hello
()
this one aligns to the center
First to do the first version of the code I mean it works it does what it needs to do, all manual like the first time I did it in arduino
And second
To understand everything we've done, which includes more or less understanding what's going on in the module
And then on Sunday make the final version of the LCD so I can finish it off one micro component less
I hope at least(?
At least the other two should be easier, keyboard it's reaaaally possible without a library if there's ever one it's basically all done
And the other things is just sending a one to a pin when a condition is met
Not even a need for a library or something fancy
So this is literally the worst it will get (?
(Until RFID)
Phew
Last question
Basically all that stuff with did with the module to handle I2C wA only so we could pass it as an argument to Display and now on we are only using the library which would be internally using our module?
yep
And when we are going to do the last part say when we are gonna avoid doing the manual work on main like the final version, we are making another module?
Or a library crate?
yep thats the idea
When this is finally done it's gonna be proud worth it(?
Tho I'm afraid on how's working in the RFID gonna be
I'm begging to God there's not only an RFID library
Is it a MFRC522 ?
But also that it includes how to deal with the SPI protocol
Yes, the read/write one.
MFRC522 driver
It should work because it uses embedded_hal
It is already hard with a library, without it, it's giving up worthy
I'm so excited to get there tho
And see how is it
And also that my rust version surpases my original C++ version
Which isn't hard because that version is stuck and idk what to do
That's one of the reasons I do this
But I would likely do it even if it was finished(?
Anyways good night, I think?
Once again thank you very much
I hope tomorrow's easier
happy to help, now 💤
Tho what can I say this is exciting anyways
Ok now I can work again
If I recall we can finally just start coding right?
Tho I forgot yesterday to ask you one thing about main, I asked you all about how to use the lcd and how the library worked an all but I didn't ask you about the serial port
And also..
That documentation we had yesterday that I thought it was merely of the LCD, is actually the Arduino-hal documentation we can consult for anything not component specific?
Or maybe even a component if lucky?
yep it has all the documentation of all the dependencies you currently have
It has all the documentation I will need for the arduino-hal abstraction or peripheral specific is elsewhere? Since this even had the LCD and I2C
yep
Well that's cool
Btw once I send my LCD function to one of my schoolmates to make he's life easier so I have the function alone with examples instead of having to show you the whole project code
I guess it should be easy to replicate in rust
struct PrintOptions {
String toPrint;
int pos;
};
const PrintOptions empty = {"", 0};
void imprimirFila(byte posY, byte posX, String fila){
if(fila != ""){
byte caracteres_restantes = 20 - fila.length();
if(caracteres_restantes<0){
lcd.print("muy largo 1");
}
byte posicionX = posX == 'c'? (caracteres_restantes/2) : posX;
lcd.setCursor(posicionX,posY);
lcd.print(fila);
}
}
void imprimir(PrintOptions fila1 = empty, PrintOptions fila2 = empty, PrintOptions fila3 = empty, PrintOptions fila4 = empty) {
imprimirFila(0, fila1.pos, fila1.toPrint);
imprimirFila(1, fila2.pos, fila2.toPrint);
imprimirFila(2, fila3.pos, fila3.toPrint);
imprimirFila(3, fila4.pos, fila4.toPrint);
}
Ejemplos:
do{
imprimir(
{ "Elija una bebida", 'c' },
{ "1-Fernet con Coca", 0 },
{ "2-Sex on the beach", 0 },
{ "3-Agua", 0 }
);
do{
imprimir(
{ "Elija una bebida", 'c' },
{ "1-Fernet con Coca", 0 },
{ "2-Sex on the beach", 0 },
{ "3-Agua", 0 }
);
imprimir(
empty,
{ "preparando...", 'c'}
);
This does everything, center the text or allow you to do manually, print stuff or tell you that's too long if you over the character limit and if you need to write only the first too lines and omit the last too there a empty constant they are all set by default but I you have a text in the center you can use empty in the first line
It has everything built into it
There's a function to print to only one line because the original way was too repetitive and there merely another function that calls the first four times
And a struct to make things easier
And also the logic is there so I guess when we have to do it, I hope it isn't that much of a challenge to re write that
doesn't look to bad
I guess we will make the print functions methods of the PrintOptions struct
Well any ways for now I have to make the original manual stuff
Tho because I'm used to it
I will likely make it in cpp fashion or something
So maybe you could then show me an idiomatic rust way maybe if needed re factor
I was going to do the exactly very same that in the original code , first set the position, then print the stuff I want, add the serial port stuff that waits for a character so it stays until you choose, then when you chose, if it's something I don't want,clear then print something like, incorrect value, clear again and return so we go back to the last menu, and when what I want is pressed I do the exact same
Just if and else asking for an specific ascii character
I thought maybe there was a functional way or doing it or that instead of an if else use a match or if let idk
An idiomatic way of rust to do it better or more rusty
Ofc if not necessary and verbose not but just to make sure I ain't taking advantage of rust's tools
In any case I'll show you my code if I make it work and then we re factor or you tell me how to do it better
Unless ofc I can't even finish it myself but I really hope I ain't that useless
Oh btw
Is there a way to add ram to a notebook?
Because I could do it if it's possible
I really want at least 8..
Depends on the laptop
I see
How I know if mine can?
A bool in rust is only the keywords true or false or it can be 1 and 0?
only true, false
You would get the model number and look it up
What's the lowest interger type?
Because I need a variable that can only be 0 or 1 so in arduino I made it bool but here is an interger, so at least I'll anotate the smallest interger possible
?eval true as u8
1
?eval 1 == 0
false
Aside from that I have an issue, seems like nb::block gives back an u8
Not a character
And I should compare it to a character
Well I guess here it doesn't matter. .
Yeah I guess not
?eval 'a' as u8
97
?eval 97 as char
'a'
So an u8 is the smallest interger possible?
yep it has 8 bits
Bruh I'm used to variables being mutable by default I forgot mut
?eval println!("{:b}", 42);
101010
()
lcd.clear() was the method to clear?
yep
How was delay?
arduino_hal::delay_ms(...)
Thanks
Ok now I'm gonna show you the first bit of code the one that prints the first menu
Since well the other ones are gonna be basically the same
And you tell me if it looks fine for you or how can we re factor, make it better, more idiomatic rust or smth, idk, maybe it's fine as is, 0 error only 1 warning that I think is irrelevant
warnings are always relevant 
#![deny(warnings)]
(joke)
#![no_std]
#![no_main]
mod lcd;
use crate::lcd::*;
use ::lcd::*;
use core::fmt::Write;
use arduino_hal::prelude::*;
use embedded_hal::prelude::_embedded_hal_serial_Read;
use panic_halt as _;
#[arduino_hal::entry]
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
let mut confirmacion: u8 = 0;
let mut serial = arduino_hal::Usart::new(
dp.USART0,
pins.d0,
pins.d1.into_output(),
arduino_hal::usart::Baudrate::new(57600),
);
let i2c = arduino_hal::I2c::new(
dp.TWI,
pins.d20.into_pull_up_input(),
pins.d21.into_pull_up_input(),
50000,
);
let lcd_conn = LcdConnection::new(i2c);
let mut lcd = Display::new(lcd_conn);
lcd.init(FunctionLine::Line2, FunctionDots::Dots5x8);
lcd.display(
DisplayMode::DisplayOn,
DisplayCursor::CursorOff,
DisplayBlink::BlinkOff);
lcd.entry_mode(EntryModeDirection::EntryRight, EntryModeShift::NoShift);
loop {
lcd.position(3, 0);
write!(&mut lcd, "Elija una bebida").unwrap();
lcd.position(1, 1);
write!(&mut lcd, "1-Fernet con Coca").unwrap();
lcd.position(1, 2);
write!(&mut lcd, "2-Sex on the beach").unwrap();
lcd.position(1, 3);
write!(&mut lcd, "3-Agua").unwrap();
let tecla = nb::block!(serial.read()).void_unwrap();
if tecla == 1{
confirmacion = 1;
} else if tecla == 2||tecla == 3 {
lcd.clear();
lcd.position(3, 0);
write!(&mut lcd, "Bebida").unwrap();
lcd.position(3, 1);
write!(&mut lcd, "No disponible").unwrap();
confirmacion = 0;
arduino_hal::delay_ms(1000);
}
}
}
For now it's like that
Before the loop only new thing is the confirmacion variable
Ah.. I forgot an else for the not valid value when it isn't 1,2 or 3
Well anyways if that's fine the else I'm gonna add too
I will go add it in the meantime
Actually..
I also forgot the while loop
That breaks when confirmation is 1
Here is an idea
loop {
lcd.position(0, 0);
write!(&mut lcd, "{:^20}", "Elija una bebida").unwrap();
lcd.position(0, 1);
write!(&mut lcd, " 1-Fernet con Coca").unwrap();
lcd.position(0, 2);
write!(&mut lcd, " 2-Sex on the beach").unwrap();
lcd.position(0, 3);
write!(&mut lcd, " 3-Agua").unwrap();
let tecla = nb::block!(serial.read()).void_unwrap();
match tecla as char {
'1' => confirmacion = 1,
'2' | '3' => {
lcd.clear();
lcd.position(0, 0);
write!(&mut lcd, "{:^20}", "Bebida").unwrap();
lcd.position(0, 1);
write!(&mut lcd, " No disponible").unwrap();
confirmacion = 0;
arduino_hal::delay_ms(1000);
},
_ => {},
}
}
No, but yes
here is the else i was missing
Its cursed
else {
lcd.clear();
lcd.position(3, 0);
write!(&mut lcd, "elija un").unwrap();
lcd.position(3, 1);
write!(&mut lcd, "valor valido").unwrap();
confirmacion = 0;
arduino_hal::delay_ms(1000);
}
But I need a do while loop like in the original for this to be complete
Now I'll see what you came up with
I like it
Tho I don't understand why you use that weird center syntax if position already takes care of it
🤷♂️ Just throwing some ideas in
So to add the else I just send
The catch all arm
yep
Could have that code
Perfect
Now that only thing is replacing the do while
So confirmation is actually useful
But first let me re factor with your solution
Ok I pass in the new version to make sure I did the changes correctly
#![no_std]
#![no_main]
mod lcd;
use crate::lcd::*;
use ::lcd::*;
use core::fmt::Write;
use arduino_hal::prelude::*;
use embedded_hal::prelude::_embedded_hal_serial_Read;
use panic_halt as _;
#[arduino_hal::entry]
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
let mut confirmacion: u8 = 0;
let mut serial = arduino_hal::Usart::new(
dp.USART0,
pins.d0,
pins.d1.into_output(),
arduino_hal::usart::Baudrate::new(57600),
);
let i2c = arduino_hal::I2c::new(
dp.TWI,
pins.d20.into_pull_up_input(),
pins.d21.into_pull_up_input(),
50000,
);
let lcd_conn = LcdConnection::new(i2c);
let mut lcd = Display::new(lcd_conn);
lcd.init(FunctionLine::Line2, FunctionDots::Dots5x8);
lcd.display(
DisplayMode::DisplayOn,
DisplayCursor::CursorOff,
DisplayBlink::BlinkOff);
lcd.entry_mode(EntryModeDirection::EntryRight, EntryModeShift::NoShift);
loop {
lcd.position(0, 0);
write!(&mut lcd, "Elija una bebida").unwrap();
lcd.position(0, 1);
write!(&mut lcd, " 1-Fernet con Coca").unwrap();
lcd.position(0, 2);
write!(&mut lcd, " 2-Sex on the beach").unwrap();
lcd.position(0, 3);
write!(&mut lcd, " 3-Agua").unwrap();
let tecla = nb::block!(serial.read()).void_unwrap();
match tecla as char {
'1' => confirmacion = 1,
'2' | '3' => {
lcd.clear();
lcd.position(0, 0);
write!(&mut lcd, "Bebida").unwrap();
lcd.position(0, 1);
write!(&mut lcd, " No disponible").unwrap();
confirmacion = 0;
arduino_hal::delay_ms(1000);
},
_ => {
lcd.clear();
lcd.position(3, 0);
write!(&mut lcd, "elija un").unwrap();
lcd.position(3, 1);
write!(&mut lcd, "valor valido").unwrap();
confirmacion = 0;
arduino_hal::delay_ms(1000);
},
}
}
}
Is it ok?
looks good
Ok so now the thing is the internal loop
See, in the original version
I used a do while so
It first executed this whole thing
And then asked for confirmation
How do we do it here?
Just for reference I'll show the cpp equivalent or what we are doing
loop {
// do stuff
if something {
break;
}
}
void loop(){
do{
lcd.setCursor(3,0);
lcd.print("Elija una bebida");
lcd.setCursor(1,1);
lcd.print("1-Fernet con Coca");
lcd.setCursor(1,2);
lcd.print("2-Sex on the beach");
lcd.setCursor(1,3);
lcd.print("3-Agua");
tecla=keypad.waitForKey();
if(tecla=='1'){
confirmacion=1;
}
else if(tecla=='2'||tecla=='3'){
lcd.clear();
lcd.setCursor(3,0);
lcd.print("Bebida");
lcd.setCursor(3,1);
lcd.print("No disponible");
confirmacion=0;
delay(1000);
}
else{
lcd.clear();
lcd.setCursor(3,0);
lcd.print("elija un");
lcd.setCursor(3,1);
lcd.print("valor valido");
confirmacion=0;
delay(1000);
}
} while(confirmacion!=1);
Mmmm so we are going to use the loop keyword instead of a while loop?
yep, and you can actually move the break to where confirmacion = 1 and then remove confirmacion completely
yep
?eval break rust;
error[E0425]: cannot find value `rust` in this scope
--> src/main.rs:2:7
|
2 | break rust;
| ^^^^ not found in this scope
error[E0268]: `break` outside of a loop
--> src/main.rs:2:1
|
2 | break rust;
| ^^^^^^^^^^ cannot `break` outside of a loop
error: internal compiler error: It looks like you're trying to break rust; would you like some ICE?
note: the compiler expectedly panicked. this is a feature.
note: we would appreciate a joke overview: https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675
note: rustc 1.64.0-nightly (3924dac7b 2022-07-29) running on x86_64-unknown-linux-gnu
Some errors have detailed explanations: E0268, E0425.
For more information about an error, try `rustc --explain E0268`.

Well I had no error but I still have to check if it's done correctly
Well about the indentation of brackets idk but
I think it's okay
I'll send it again if it's okay to you we can already test if this works before moving forward
This time I'm only sending loop
You know the stuff before and that confirmation is gone now
loop {
loop{
lcd.position(0, 0);
write!(&mut lcd, "Elija una bebida").unwrap();
lcd.position(0, 1);
write!(&mut lcd, " 1-Fernet con Coca").unwrap();
lcd.position(0, 2);
write!(&mut lcd, " 2-Sex on the beach").unwrap();
lcd.position(0, 3);
write!(&mut lcd, " 3-Agua").unwrap();
let tecla = nb::block!(serial.read()).void_unwrap();
match tecla as char {
'1' => break,
'2' | '3' => {
lcd.clear();
lcd.position(0, 0);
write!(&mut lcd, "Bebida").unwrap();
lcd.position(0, 1);
write!(&mut lcd, " No disponible").unwrap();
arduino_hal::delay_ms(1000);
},
_ => {
lcd.clear();
lcd.position(3, 0);
write!(&mut lcd, "elija un").unwrap();
lcd.position(3, 1);
write!(&mut lcd, "valor valido").unwrap();
arduino_hal::delay_ms(1000);
},
}
}
}
}
looks good
Ok let's stop to think what would happen
So.
This should turn on
Write all that stuff that covers the four lines
And then wait for me to write whatever in the terminal
If 1 it exist the loop and then idk actually we should put a clear outside the loop or idk how can we tell
But if 2 or 3, print that it's not available and then come back to the other text and the same with any other value but it's just another message
So how do me test we exit the loop successfully with one?
Leaving that question I need to go dinner, I'll be back soon
you could print something to the serial console
Oh sure
Btw I'm back
Ok now I can test it
But first I need this again
cargo run -- --port COM6 -c
Mmm.. .
When it turns one that goes well
But say I send 4
It shows the text of unvalid value
Three times
A first and it quickly goes back to original text but then goes back to unvalid and then the original
And so 3 times
If I write 2
It does the same but it blinks also to the not valid portion
Drink not available, then the first 3 options, then unvalid value 3 times
It's a really weird behavior
Do you have to press enter in the terminal to send the number?
And one and one does unvalid value and then it prints the I left the loop thing I placed
Yes that's normal at least in the arduino IDE
Ah, it's getting [number][new line]
the ravedude console doesn't filter out newlines
I don't get it
instead of getting one character it gets 2
Why is that?
Because you press enter which is a character
And also it does it three times rather than two
Oh
Also
The centering is not working properly
There are extra spaces in the strings
Oh I'll solve it
Ok now I'll test the behavior again to make sure of something
I'll press 1 first and the re run
fn read_char(serial: &mut arduino_hal::usart::Usart) -> char {
let mut last_char = ' ';
loop {
let current_char = nb::block!(serial.read()).void_unwrap() as char;
if current_char == '\n' {
return last_char;
} else {
last_char = current_char;
}
}
}
K
What it did was
First
It took a long time to react
Second
It excited the loop but then the unvalid value blink 2 times
So it's like it's taking 3 chars
The correct 1 and other 2
it may be using \r\n instead of just \n
Probably
What's that?
I wrote you a function to read one char
Two questions
First
I write it in place of nb::block and set tecla to the result of calling the function?
fn read_char(serial: &mut arduino_hal::usart::Usart) -> char {
loop {
let current_char = nb::block!(serial.read()).void_unwrap() as char;
if current_char != '\n' && current_char != '\r' {
return current_char;
}
}
}
this one should work with the \r\n
.
yep
It loops until it gets a non new line character (not \r or \n) and then returns it
Should I write this signature just before the loop and then call it in that other line, right?
you put it outside of the main function
Why are there two US arts, what is each one?
US arts?
Usarts*
there is only one
&mut arduino_hal::usart::Usart
error[E0107]: missing generics for type alias `arduino_hal::Usart`
--> src\main.rs:12:47
|
12 | fn read_char(serial: &mut arduino_hal::usart::Usart) -> char {
| ^^^^^ expected 3 generic arguments
|
note: type alias defined here, with 3 generic parameters: `USART`, `RX`, `TX`
--> C:\Users\fuchi\.cargo\git\checkouts\avr-hal-f3855d5807fdfd57\1aacefb\arduino-hal\src\lib.rs:163:14
|
163 | pub type Usart<USART, RX, TX> = crate::hal::usart::Usart<USART, RX, TX, crate::DefaultClock>;
| ^^^^^ ----- -- --
help: add missing generic arguments
|
12 | fn read_char(serial: &mut arduino_hal::usart::Usart<USART, RX, TX>) -> char {
| ~~~~~~~~~~~~~~~~~~~~
For more information about this error, try `rustc --explain E0107`.
when you call it pass the one in main,
let tecla = read_char(&mut serial);
What I mean is I want to understand the signature
Why are there two things called almost the same what is that me are receiving exactly
usart is the module, Usart is the struct, USART is a generic type
Oh ok so we are calling from the arduino hal abstraction the usart module that has the Usart struct
Why is USART a generic type?
Its generic over the Usart device/pins
And Usart would be the serial port or what exactly?
The mega has 4 usart devices
yeah USART0 is the one connected to the computer
So usart is a serial port which the MEGA has four of the them and that's why USART is generic?
use embedded_hal::serial::Read;
fn read_char(serial: &mut impl Read<u8>) -> char {
loop {
let current_char = nb::block!(serial.read()).unwrap() as char;
if current_char != '\n' && current_char != '\r' {
return current_char;
}
}
}
this should fix the error
We can just use the trait Read instead
yep
And you store in the variable current charity the value of nb::block which since it's an u8 you cast it to a char with as keyword, and the if makes sure you don't return anything else that the first only char, right?
yep
It's necessary to use Return? I thought in rust it wasn't necessary
here, we are in a loop so we need to manually break out of it
I copy pasted wrong remove the "T:"
Well there are more errors let me run cargo check
error[E0277]: `<impl Read<u8> as _embedded_hal_serial_Read<u8>>::Error` doesn't implement `Debug`
--> src\main.rs:15:54
|
15 | let current_char = nb::block!(serial.read()).unwrap() as char;
| ^^^^^^ `<impl Read<u8> as _embedded_hal_serial_Read<u8>>::Error` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
= help: the trait `Debug` is not implemented for `<impl Read<u8> as _embedded_hal_serial_Read<u8>>::Error`
note: required by a bound in `Result::<T, E>::unwrap`
--> C:\Users\fuchi\.rustup\toolchains\nightly-2022-06-13-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\result.rs:1094:12
|
1094 | E: fmt::Debug,
| ^^^^^^^^^^ required by this bound in `Result::<T, E>::unwrap`
help: introduce a type parameter with a trait bound instead of using `impl Trait`
|
13 | fn read_char<R: Read<u8>>(serial: &mut R) -> char where <R as _embedded_hal_serial_Read<u8>>::Error: Debug {
| +++++++++++++ ~ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
warning: unused import: `embedded_hal::prelude::_embedded_hal_serial_Read`
--> src\main.rs:8:5
|
8 | use embedded_hal::prelude::_embedded_hal_serial_Read;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
= note: `#[warn(unused_imports)]` on by default
For more information about this error, try `rustc --explain E0277`.
warning: `rust_lcd` (bin "rust_lcd") generated 1 warning
use embedded_hal::serial::Read;
fn read_char(serial: &mut impl T: Read<u8>) -> char {
loop {
if let Ok(current_char) = nb::block!(serial.read()) {
let current_char = current_char as char;
if current_char != '\n' && current_char != '\r' {
return current_char;
}
}
}
}
This has to do with the Void thing
What void thing?
I was later gonna ask about that
btw it says this is an unused import, do we get rid of it?
use embedded_hal::prelude::_embedded_hal_serial_Read;
But then why did we had to change it?
Because before we just needed it in scope to call .read(). Now we need to actually use the type name
No errors neither warnings so I'll test it
Btw I want to know about what changes, what are we receiving now as a parameter and why does it make it work?
Because the Usart is generic over a few things, I didn't want to go figure out what they were. So instead we are using the Read trait
The impl Read<u8> says it will take anything that implements Read<u8>
basically you can read bytes from it
It works perfectly just that sometimes the answer takes time to arrive but maybe is my PC the thing
I have to enter my dogs home wait for me a little bit but anyways it works so we can finish the LCD programm now ig
I'm back
Oh ok I understand
Well now let's keep working
Tho
Yesterday I didn't ask you about how this whole serial port deal worked, in order to understand main 100% I need to ask
First
let mut serial = arduino_hal::Usart::new(
dp.USART0,
pins.d0,
pins.d1.into_output(),
arduino_hal::usart::Baudrate::new(57600),
);
About this
First, what's the serial variable? The serial port of choice because the MEGA has four and we specificy inside we want to use the computer one?
yep
And d0 and d1 are the pins it is connected to and the 57600 is the speed
Ok so you create a new instance or the Usart strict which we already stated what it was
But what's that of
dp.USART0?
So that's telling use the serial port at 0 which is the computer one?
yep
I get the speed part because we talked about it before
But what are the other 2 pins for, what do they do if we said what port are we using? There's nothing conected to port 0 and 1
pin*
pins 0 and 1 are connected internally to the usb chip on the board
the chip the arrow is pointing to
So let's say they are needed for all serial communication so they are always gonna be the arguments of Usart new?
yep, at least for this project
So 0 receives data from the serial port and 1 sends it to the serial port of choice so because of that is an output, right?
yep
you will notice the TX and RX LEDs on the board flash when you send stuff over the USART0
as they are also connected to d0 and d1
Next line I wanted to talk about
ufmt::uwriteln!(&mut serial, "Sali del loop").void_unwrap();
What's ufmt it calls my attention
Its a smaller version of write!
But write! Wasn't for the LCD?
I think you can use write! on the Display, not 100% sure
try it out and see if it has an error
But we use write we don't use anything else
Remember today I said it called my attention that we used a macro and not an LCD method
Yesterday*
oh, yeah I ment use write! on the serial, or use ufmt::write!() on the Display
Why we use uwriteln? And what do you mean a smaller version?
"μfmt, a (6-40x) smaller, (2-9x) faster and panic-free alternative to core::fmt" https://docs.rs/ufmt/latest/ufmt/
API documentation for the Rust ufmt crate.
What's ufmt? I guess fmt is from format, it's a library tho? A module?
its a library its "micro-format"
So there's a library format and a micro format two separate ones?
yeah, the one in core results in a bigger binary, but compiles faster
fmt it's the one in core?
yep
And where's the other one from?
So the other one takes longer to compile but it's made so the binary results smaller?
Utilities for formatting and printing strings.
yep
So you could use both in place of one another?
yep,
Tho we never included fmt
Only fmt
mod lcd;
use crate::lcd::*;
use ::lcd::*;
use core::fmt::Write;
use arduino_hal::prelude::*;
use embedded_hal::serial::Read;
because fmt is in core it is automatically available
Sorry I mean
As you see there
We include write from fmt
But we never included
Ufmt
To use
ah, thats because we just named it directly with ufmt::...
So it's in the core too so there's no need to bring into scope while with fmt we did need to bring write macro into scope?
yeah
And I guess the u in uwriteln is for micro, and it's weird that one is just write and the other writeln but that's irrelevant
So it takes a mutable references to our serial port and the string it prints to it
I get that but
Why void_unwrap?
What does it do?
If it fails not panic but rather do nothing?
It makes a Result<T, Void> into a T without ever panicking because Void can't actually exist
enum Void {}
Thats the definition of Void
Because it's an enum with no variants there is no way to make one
So uwriteln return a Result <T, Void> because it can never fail and we call instead of just unwrap, void_unwrap to take the T out of the result enum it gives back?
Why does it return a result type if literally you are doing nothing with it and go for the extra steps of extracting T?
because it is generic, some things that implement the Write trait may fail. Just this one doesn't
Oh ok
Btw in the meantime of talking about this
I wrote the rest of the code
Let's see if it's done and it works
It perfectly works I just forgot about a detail
Yey :")
It's finished
I'll send it so you see it and tell if we refactor anything or there's room for improvement
Ok here
#![no_std]
#![no_main]
mod lcd;
use crate::lcd::*;
use ::lcd::*;
use core::fmt::Write;
use arduino_hal::prelude::*;
use embedded_hal::serial::Read;
use panic_halt as _;
fn read_char(serial: &mut impl Read<u8>) -> char {
loop {
if let Ok(current_char) = nb::block!(serial.read()) {
let current_char = current_char as char;
if current_char != '\n' && current_char != '\r' {
return current_char;
}
}
}
}
#[arduino_hal::entry]
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
let mut serial = arduino_hal::Usart::new(
dp.USART0,
pins.d0,
pins.d1.into_output(),
arduino_hal::usart::Baudrate::new(57600),
);
let i2c = arduino_hal::I2c::new(
dp.TWI,
pins.d20.into_pull_up_input(),
pins.d21.into_pull_up_input(),
50000,
);
let lcd_conn = LcdConnection::new(i2c);
let mut lcd = Display::new(lcd_conn);
lcd.init(FunctionLine::Line2, FunctionDots::Dots5x8);
lcd.entry_mode(EntryModeDirection::EntryRight, EntryModeShift::NoShift);
loop {
loop{
lcd.display(
DisplayMode::DisplayOn,
DisplayCursor::CursorOff,
DisplayBlink::BlinkOff);
lcd.position(3, 0);
write!(&mut lcd,"Elija una bebida").unwrap();
lcd.position(1, 1);
write!(&mut lcd, "1-Fernet con Coca").unwrap();
lcd.position(1, 2);
write!(&mut lcd, "2-Sex on the beach").unwrap();
lcd.position(1, 3);
write!(&mut lcd, "3-Agua").unwrap();
let tecla = read_char(&mut serial);
match tecla as char {
'1' => break,
'2' | '3' => {
lcd.clear();
lcd.position(3, 0);
write!(&mut lcd, "Bebida").unwrap();
lcd.position(3, 1);
write!(&mut lcd, "No disponible").unwrap();
arduino_hal::delay_ms(1000);
},
_ => {
lcd.clear();
lcd.position(3, 0);
write!(&mut lcd,"elija un").unwrap();
lcd.position(3, 1);
write!(&mut lcd,"valor valido").unwrap();
arduino_hal::delay_ms(1000);
},
}
}
ufmt::uwriteln!(&mut serial, "Sali del primer loop").void_unwrap();
loop{
lcd.clear();
lcd.position(0, 0);
write!(&mut lcd,"Elija la intensidad").unwrap();
lcd.position(1, 1);
write!(&mut lcd, "1-Suave").unwrap();
lcd.position(1, 2);
write!(&mut lcd, "2-Medio").unwrap();
lcd.position(1, 3);
write!(&mut lcd, "3-Fuerte").unwrap();
let tecla = read_char(&mut serial);
match tecla as char {
'1' | '2' | '3' => break,
_ => {
lcd.clear();
lcd.position(3, 0);
write!(&mut lcd,"elija un").unwrap();
lcd.position(3, 1);
write!(&mut lcd,"valor valido").unwrap();
arduino_hal::delay_ms(1000);
},
}
}
ufmt::uwriteln!(&mut serial, "Sali del segundo loop").void_unwrap();
loop{
lcd.clear();
lcd.position(3, 1);
write!(&mut lcd,"Preparando...").unwrap();
lcd.display(
DisplayMode::DisplayOn,
DisplayCursor::CursorOn,
DisplayBlink::BlinkOn);
arduino_hal::delay_ms(10000);
break;
}
ufmt::uwriteln!(&mut serial, "Sali del tercer loop").void_unwrap();
}
}
There
Tho I can remove the uwriteln now
As you'll see lcd.display it's move to loop because I want that the last thing is printed by the lcd which is a text for the period of time the drink is being served, I wanted the cursor to appear an blink so at the top of loop of put that so when it goes to the beginning again it goes away and remove it from main to avoid repetition
Actually, I'll keep them for you now the part of making the function and all maybe, and the remove just at the very end
Oh and also I have two final questions about that code whenever it needs refactoring or not
Basically finish the understanding the serial port usage , we went through the serial variable and the uwriteln macro, but we still have to see the read one and also I want to see if I understand the if let statement you did because I have a hard time still understanding if let syntax
This syntax means 'do nb::block!(Serial.read()) and if it results in an Ok, take what's inside the Ok as the variable current_char, and make current_char available within the if block'
The if let X(y) = <expression> { <operations with y> } syntax is basically equivalent to
match <expression> {
X(y) => { <operations with y> }
_ => ()
}
Thank you
Tho aside from understanding the if let syntax the other thing I wanted was to ask for how nb::block!(Serial.read()) worked
@coral geyser good afternoon (I think?) are you available?
👋 for a little bit
Later today I am busy, just thought I would pop in to the discord and answer some questions between tasks
Oh, well I'm going to sleep early because I have school tomorrow but I hope coincidentally with the hour difference I get to see you a little bit free
Well then if you have a little bit of time
I have two questions primarily
The code above works as intended, but do you have any feedback on how to do it better or refactor it just like when you changed my if else to match? Or you would leave it like that?
One thing that comes to mind is it would probably make it easier to read if you made functions for each of the screens
fn show_main_menu(lcd: &mut Display<LcdConnection>) {
...
}
So the main_menu(to call it different to the next one) select menu and wait menu?
Tho in future parts of the project there will be more selection menus so, it will work only for now to call it main menu
So I create three functions in another module, copy paste the code is have in them , import it, and merely call them in main?
I have*
Oh also looks like you are suffering from a lot of states for the program. (Which loop it is in) You should really use a state machine to track that https://barrgroup.com/embedded-systems/how-to/coding-state-machines
A state machine is any object that behaves different based on its history and current inputs. Many embedded systems consist of a collection of state machines
Yep
What would the signature be, they will all take mutable references to lcd, and return nothing?
Yeah, they will just print the 4 lines to the screen that you need for each one
And I can call display for the setup like I did with no problem inside them, right?
And also, I would create a module or a library crate to do this? And how should it be called? I propose we maybe should call the lcd module (which takes cares of the I2C) I2C and this new module lcd maybe
For now you could just put them in the main.rs file then move them later
No, you can put them were ever you want. General rule is to refactor in small moves
Oh so maybe when I prove they work as intended I could do that?
And also are you fine with the name changes or are they wrong?
Everything looks good 🤷♂️
The new functions should also be in a module or should they be in a library crate?
Just a module
You probably don't need any custom library crates for this project
When do you know if a library or a module is needed?
Make a library crate it another project will use the code
Also, when we do the last step remaining which is you now the function to stop making things manually and so, will it also be in it's own module?
Well I was going to make one project for each component and them a new one for all combined or you say we should in this we are already working add the new stuff?
This is where git would be handy, as you could save the state of the project at each step
I mean I could ask you to teach and we can work there but wouldn't it be a pain?
Yeah probably
And also can you run code there or we will use it just to save the code?
I would just copy all the code when you make a new project
Well in some clases of my python course I will have git because likely we are updating our project there
It should be easier the manage than making library crates
Oh ok that's fine for me
.
It can be in it's own module if it makes sense to you to be
I think I don't have the criteria to know that yet
My code it's messy
Well then what could we call the functions?
Last one is waiting screen
But the first two are both selection screens
Maybe.. Yeah, select drink, select intensity
Well after doing that of the functions and let's imagine my code works again like intended
Would you do anything else or we could go to the last step?
Think about using a state machine ^
Aaaah yeah that I forgot
I don't get that about problem with states and what a state machine is and all that
And also that says C, C++ is just for knowing the concept?
Basically you would have like an enum saying which screen (state) you are in. And one loop that moves between states
Yep
Oh ok
So..
An enum
With all my states
Like, waiting, select drink, select intensity
And my loop single loop which ask the state and execute the function depending on the state and at the end of each function I change the state to call the next function?
Yep, it may also contain things like: the drink selected after you enter it, the intensity after you enter it...
Yep
I think I will need help for that so I'll leave it for later, but the functions in it's own module I'll do it in a little bit
The other question I had beside the whole refactoring was
struct App {
state: State,
selected_drink: Drink,
amount: u8,
...
}
nb::block!(Serial.read())
That line
So I can finally understand all we do with the serial port
First, what's nb? A module, a library?
It's a library
I can make a little attempt but I better leave that for when you are free
It provides no-block (nb) input/output APIs
What does the block macro do and what does it recieve as an argument?
So like in the Arduino code you know how you have the available byte count on the serial to see if there is stuff to read?
Yeah, what that does is ask if there's something to read and if 0 there's nothing yet so I used in while with the condition it had to be bigger than 0
The serial.read() can return a nb::Error::WouldBlock
This signals that there isn't anything to read yet
The block!() macro just waits for this to become an actual value
So serial read does the same as serial available and while it has nothing to read it returns an error and when it receives something it return it?
Yep
So you would use the block macro with anything that returns a would block so you can block the program until there's something
Exactly
You could also not use block!() And wait manually if you wanted to do something while you wait
The last thing I would like to ask before jumping into code again aside from how to do the state machine exactly (which I would the code in front of me from and Start doing stuff which would take time) was to go over the lcd module we made so I could understand what we did with I2C to understand to protect to it's fullest it's not a long code but idk if this will take long or not so if you think is too much we could do it for later that also
I need to get going now
Oh well good look, I hope I see you later before I go to sleep so maybe maybe we could finally get the lcd 100% done, in the meantime I will do the functions and update here if they worked or what happened
Luck*
I am doing the functions and so
and got like millions errors
I thought the argument should be a mutable reference to the lcd we defined
so I wrote all signatures like this
fn show_drinks(lcd: &mut lcd)
It seems it doesn't work
But then I saw when you made an example
You made this the parameter
lcd: &mut Display<LcdConnection>
I have no clue why and why mine's wrong but I'll see if writing it that way fixes all the errors because it seems they mostly have to do with scope and the setup I did in main missing there is as if I had to copy everything from main and main would only have the call to that functions
Well maybe my signature wasn't incorrect
Thing is..
I should take everything from main and place it there? Even the dp definition? But then I wouldn't be able to work in main with other components without dp and pins
I don't understand I copied dp, pins, serial definition, i2c, definition, ldc_conn definition, and lcd and lcd.init, entry mode
Everything
And it's full of errors
It seems that kind of thing in which the error is really stupid but I don't get what's happening
I imported some use statements I see if I need more and that help solve some of them
Well there are still erorrs I can't understand
I'm making this follow up just in case
I got rid of the module it couldn't find that worked for some reason and I for rid of all the copying of the variables like dp and so
But then it said can't find serial so ok I brought serial definition
And now this
Idk what's an item
And why here let it's supposedly wrong and not before
Is a variable after all
And i don't also now what's "BANG"
error: expected item, found keyword `let`
--> src\lcd.rs:19:1
|
19 | let mut serial = arduino_hal::Usart::new(
| ^^^ expected item
error: could not compile `rust_lcd` due to previous error
PS C:\Users\fuchi\OneDrive\Documentos\Codigos_Rust\rust_lcd> cargo check
Checking rust_lcd v0.1.0 (C:\Users\fuchi\OneDrive\Documentos\Codigos_Rust\rust_lcd)
error: expected item, found keyword `let`
--> src\lcd.rs:18:1
|
18 | let mut serial = arduino_hal::Usart::new(
| ^^^ expected item
error: could not compile `rust_lcd` due to previous error
I think this is as far as I can get I can't make sense of this error
Just for the record if that helps
I haven't deleted anything from main just copied it here until it worked and then I could try but I don't think that has to do
If i interpreted your screen photos correctly, the issue is that you can't have let statements outside of functions
In fact, a lot of your errors just like stuff that should be inside functions being outside functions
I think in this case, the 'expected BANG' thing is the same; bang refers to the ! character, so i think rust saw identifiers in the top-level scope and assumed they were macros (and thus expected them to end with a !), but got confused when there wasn't any
So it worked in the original file because it was inside of main and here it doesn't because it's in like global scope?
And since I can't declare for example serial outside the function I couldn't use it
So maybe I could declare serial at the beginning of the function that uses it
Or create another main which would be weird for a module to extract code from the main function-
And turn it into functions too
What might be better (not sure how the hal handles things) is to create the serial in your main, and then just pass a ref of that to all the functions that need it
Since it feels like reopening the serial line in every function could be problematic
Oh that could work :o
I'll see if I can also pass a reference to serial to the functions that needed
Thank you when I'm back from school I'll try that, and also now I know that I can't declare variables outside a function so I should pass references to things declared in main
Since you are here, could you more or less explain to me the state machine thing? The one who's helping me told I had a problem with state, which loop my programm is in, and I should do a state machine that was like an enum that tracked the state of my programm or idk, smth like that and even made a little example but I don't know exactly what to do, how to implement it
You could scroll up to see well the code (basically it's still like that excepting for the module I was trying to do to make it more readable and the I2C module which is irrelevant for this situation)
Yeah; for completeness, i should mention that statics would also work here, though I've often found it simpler to pass stuff around, since mutability and statics don't mix super elegantly in rust, so I'd check with the other person about that, since they have a better understanding of your requirements
I think this is just thinking of your program as a set of states it can be in, and a loop where each iteration moves from one state to another
So for example, you might go from State::DisplayingMainMenu to State::PouringDrink(DrinkType::Agua, /* amount: */12) based on the result of your show_main_menu function or whatever you have
The above would be an example where the state is an enum, where each variant (and its associated info) is a state; of course, there are different ways to distribute that information; for instance, you might move certain variables that are common to most/all states into a struct that wraps the state enum, as i believe madfrog suggested earlier, but the exact form that takes is whatever you think will support the operations you need your program to perform
So basically you have a loop of «initial state -> perform operation appropriate for that state -> set new state based on the result of that operation -> perform operation appropriate for that state -> ...»
Anyway, I'm heading off to sleep; buena suerte con el proyecto!
Muchas gracias
Tho I will needed to go over the state machine in code to see if I understand
I understand the concept
Maybe not so much how you apply it
@coral geyser are you up for today? (Is that a valid english expression?)
I am finally actually free
Not me :(
I have my grandpas birthday
I may have some time to work if I stay awake a bit idk when it's ending but not that late
I reaaaally hope you can still be here by then
In the meantime well
I had some issues with the module but maybe by passing a reference to serial it's resolved I hope
And yeah I get the concept but I will need help to actually implement a state machine
Okay
And well then the only thing left is making this function in rust to have it even easier to use it later
struct PrintOptions {
String toPrint;
int pos;
};
const PrintOptions empty = {"", 0};
void imprimirFila(byte posY, byte posX, String fila){
if(fila != ""){
byte caracteres_restantes = 20 - fila.length();
if(caracteres_restantes<0){
lcd.print("muy largo 1");
}
byte posicionX = posX == 'c'? (caracteres_restantes/2) : posX;
lcd.setCursor(posicionX,posY);
lcd.print(fila);
}
}
void imprimir(PrintOptions fila1 = empty, PrintOptions fila2 = empty, PrintOptions fila3 = empty, PrintOptions fila4 = empty) {
imprimirFila(0, fila1.pos, fila1.toPrint);
imprimirFila(1, fila2.pos, fila2.toPrint);
imprimirFila(2, fila3.pos, fila3.toPrint);
imprimirFila(3, fila4.pos, fila4.toPrint);
}
Ejemplos:
do{
imprimir(
{ "Elija una bebida", 'c' },
{ "1-Fernet con Coca", 0 },
{ "2-Sex on the beach", 0 },
{ "3-Agua", 0 }
);
do{
imprimir(
{ "Elija una bebida", 'c' },
{ "1-Fernet con Coca", 0 },
{ "2-Sex on the beach", 0 },
{ "3-Agua", 0 }
);
imprimir(
empty,
{ "preparando...", 'c'}
);
Well there's actually something else but that I 100% need a little bit of time to get into
Well I'm gonna try my hardest to come as soon as possible
Because really after the lcd everything left for base functionality is fairly easy and quick or that it seems so I could still get it by Wednesday if God is by my side(?
Enjoy your time, I am planning on working on #993761711343534130 tonight, so I should be on for a while
@coral geyser I came back, now I have a decent amount of time
I broke my Gentoo install so I am just working on that now
Gentoo?
Its a form of linux
Wow
Programmers do be prepared
Have you seen more or less what I had tried to do and failed miserably?
I sort of need it as I compile my own linux kernels 😅
I only caught parts of it
I'm gonna try what that gentleman said
Of pasing a mutable reference to serial
To all those functions
To see if maybe that was it
But well if it doesn't work I'll ask you
Btw I have decided
I will soon by the raspberry Pi pick
Pico*
But the last models with wifi and Bluetooth don't enter the country :(
That's a huge disappointment for me.
You can try out some micro python when you get it
Also I don't get why some cost 1500 pesos and some others 5000 what it's the difference if there's only one
So I'm doubtful about what to buy
I know python to some more or less decent extent, have you tried micro python? How is it?
That I have no idea. I have only every bought them off the official partner sites
I have only used it a little. It's nice for what it is
Very easy to get going with it
How different is from regular python?
Not that different, mostly the standard you don't have access to the "normal" python stuff
Which basically all embedded languages suffer from
yeah, definitely
Do you like programming on it?
My tastes in embedded languages doesn't align well with micro python. I prefer very close to assembly if not actual assembly. So Rust fits nicely
Plus I do to much Python at work
Tho I heard you can
Program in micro python, in C++ or with the Arduino IDE with extra stuff, and most of the libraries work in raspberry Pi
So you can have all raspberry power and feek like home
Feel*
yep, they have done good allowing the use of other libraries
Tho idk how hard it is or how so you do it, and also how's the C++ programming option they have
Tho I'm interested in micro python for fun
I want you to help me actually get the Arduino ide there really
And also.. Could I use this abstraction we are using? I want the powerful raspberry Pi pico and rust
Actually if I could (and I'm thinking about it) I would pass my project not only to rust but to the raspberry Pi pico if has the chance
There is another HAL library for them
So it wouldn't be that hard to switch
But you mean
A raspberry Pi abstraction?
Because I was talking about since we can use the Arduino IDE there idk keep using Arduino hal?
I don't think so directly. The Arduino hal you have been using is specifically designed for AVR. The PIs run ARM Cortex instructions
But they both share the embedded_hal library so they have basically the same interface as each other in the rust code
Oh
Mmm well it also depends if I have enough pins because for example mega has like
5 Vcc and GND more or less
And also raspberry Pi is so powerful to endure something as heavy as python that rust is merely too godlike there
What would be the type of serial?
Ah yes also one thing I said earlier why in the signature of the function you placed lcd: &mut Display <LcdConnection> instead of lcd: &mut lcd?
The variable lcd was made using Display::new()
Oh you need the type
yep
For the serial one do &mut impl Write<u8>
like the Read one
It's full type is long
You probably have another Write trait in scope. We want the embedded_hal::serial::Write one
remove the core::fmt::Write for now
Resulted way more difficult that I expected
Ok
The type system takes some getting use to
It says it can't find trait write
add the use embedded_hal::serial::Write;
More myself that I'm a noob programmer
Btw is it possible to use concurrenry or async await(which I don't know what is) in embedded systems? Or is too costly for memory or it wouldn't make sense?
You can, let me go check the crate name for it
How many errors aaaaa
https://github.com/embassy-rs/embassy is the big one I believe. Its like tokio but for embedded
error[E0425]: cannot find value `serial` in this scope
--> src\lcd.rs:84:36
|
84 | let tecla = read_char(&mut serial);
| ^^^^^^ not found in this scope
warning: unused import: `arduino_hal::prelude::*`
--> src\lcd.rs:4:5
|
4 | use arduino_hal::prelude::*;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
error[E0277]: the trait bound `&mut impl Read<u8>: _embedded_hal_serial_Read<u8>` is not satisfied
--> src\lcd.rs:37:31
|
37 | let tecla = read_char(&mut serial);
| --------- -^^^^^^^^^^
| | |
| | the trait `_embedded_hal_serial_Read<u8>` is not implemented for `&mut impl Read<u8>`
| | help: consider removing the leading `&`-reference
| required by a bound introduced by this call
|
note: required by a bound in `lcd::read_char`
--> src\lcd.rs:7:32
|
7 | fn read_char(serial: &mut impl Read<u8>) -> char {
| ^^^^^^^^ required by this bound in `lcd::read_char`
For more information about an error, try `rustc --explain E0277`.
warning: `rust_lcd` (bin "rust_lcd") generated 1 warning
error: could not compile `rust_lcd` due to 2 previous errors; 1 warning emitted
first one is you don't have a serial variable for line 84
The thing is until it doesn't work in the first one I wasn't going to import it to the other functions
But well now I implemented it on all or the them
the second error, I am not sure on. do you have a use for _embedded_hal_serial_Read somewhere still?
Yes
I shall remove ir
It*
It still the unused import and that write is no longer found for the write! Because we erased it earlier
lets try switching to the uwrite!() macro for those ones now
oh, looking at the docs Display only implements core::fmt::Write 
okay nevermind
do use core::fmt::Write as _;
Why the as?
that will bring it into scope without clashing with the other Write
Cargo check again
warning: unused import: `arduino_hal::prelude::*`
--> src\lcd.rs:3:5
|
3 | use arduino_hal::prelude::*;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
error[E0277]: the trait bound `&mut impl Write<u8>: _embedded_hal_serial_Read<u8>` is not satisfied
--> src\lcd.rs:37:31
|
37 | let tecla = read_char(&mut serial);
| --------- ^^^^^^^^^^^ the trait `_embedded_hal_serial_Read<u8>` is not implemented for `&mut impl Write<u8>`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `_embedded_hal_serial_Read<Word>`:
avr_hal_generic::usart::Usart<H, USART, RX, TX, CLOCK>
avr_hal_generic::usart::UsartReader<H, USART, RX, TX, CLOCK>
note: required by a bound in `lcd::read_char`
--> src\lcd.rs:7:32
|
7 | fn read_char(serial: &mut impl Read<u8>) -> char {
| ^^^^^^^^ required by this bound in `lcd::read_char`
error[E0277]: the trait bound `&mut impl Write<u8>: _embedded_hal_serial_Read<u8>` is not satisfied
--> src\lcd.rs:84:31
|
84 | let tecla = read_char(&mut serial);
| --------- ^^^^^^^^^^^ the trait `_embedded_hal_serial_Read<u8>` is not implemented for `&mut impl Write<u8>`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `_embedded_hal_serial_Read<Word>`:
avr_hal_generic::usart::Usart<H, USART, RX, TX, CLOCK>
avr_hal_generic::usart::UsartReader<H, USART, RX, TX, CLOCK>
note: required by a bound in `lcd::read_char`
--> src\lcd.rs:7:32
|
7 | fn read_char(serial: &mut impl Read<u8>) -> char {
| ^^^^^^^^ required by this bound in `lcd::read_char`
For more information about this error, try `rustc --explain E0277`.
remove arduino_hal::prelude::* as we aren't using it right now
Done
looks like read_char is using the _embedded_hal_serial_Read trait
It should also be the embedded_hal::serial::Read
I add an use statement for this one?
well you already have it
what does the code around lcd.rs:37 look like?
let tecla = read_char(&mut serial);```
I need the whole function definition
fn show_drinks(lcd: &mut Display<LcdConnection>, serial: &mut impl Write<u8>) {
loop{
lcd.display(
DisplayMode::DisplayOn,
DisplayCursor::CursorOff,
DisplayBlink::BlinkOff);
lcd.position(3, 0);
write!(&mut lcd,"Elija una bebida").unwrap();
lcd.position(1, 1);
write!(&mut lcd, "1-Fernet con Coca").unwrap();
lcd.position(1, 2);
write!(&mut lcd, "2-Sex on the beach").unwrap();
lcd.position(1, 3);
write!(&mut lcd, "3-Agua").unwrap();
let tecla = read_char(&mut serial);
match tecla as char {
'1' => break,
'2' | '3' => {
lcd.clear();
lcd.position(3, 0);
write!(&mut lcd, "Bebida").unwrap();
lcd.position(3, 1);
write!(&mut lcd, "No disponible").unwrap();
arduino_hal::delay_ms(1000);
},
_ => {
lcd.clear();
lcd.position(3, 0);
write!(&mut lcd,"elija un").unwrap();
lcd.position(3, 1);
write!(&mut lcd,"valor valido").unwrap();
arduino_hal::delay_ms(1000);
},
}
}
}
oh, I see we don't have the Read trait there
ummm
&mut impl Write<u8> + Read<u8>
in the definition of show_drinks
Sorry, I am basing this all off the docs. So it's difficult to figure out exactly which ones you need where
this is not all but a part i guess i will do that
fn show_intensity(lcd: &mut Display<LcdConnection>, serial: &mut impl Write<u8> + Read<u8>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(impl Write<u8> + Read<u8>)`
error: ambiguous `+` in a type
--> src\lcd.rs:104:71
|
104 | fn show_waiting_screen(lcd: &mut Display<LcdConnection>, serial: &mut impl Write<u8> + Read<u8>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use parentheses to disambiguate: `(impl Write<u8> + Read<u8>)`
As if anyone else could better, there's no issue we will eventually get there (?
I'm only midly concern of not knowing for certain what I'm doing maybe
Do as the error shows and add parenthesis
Done there's one error left
Two but it's the same
error[E0277]: the trait bound `&mut impl Write<u8> + Read<u8>: _embedded_hal_serial_Read<u8>` is not satisfied
--> src\lcd.rs:36:31
|
36 | let tecla = read_char(&mut serial);
| --------- -^^^^^^^^^^
| | |
| | the trait `_embedded_hal_serial_Read<u8>` is not implemented for `&mut impl Write<u8> + Read<u8>`
| | help: consider removing the leading `&`-reference
| required by a bound introduced by this call
|
note: required by a bound in `lcd::read_char`
--> src\lcd.rs:6:32
|
6 | fn read_char(serial: &mut impl Read<u8>) -> char {
| ^^^^^^^^ required by this bound in `lcd::read_char`
error[E0277]: the trait bound `&mut impl Write<u8> + Read<u8>: _embedded_hal_serial_Read<u8>` is not satisfied
--> src\lcd.rs:83:31
|
83 | let tecla = read_char(&mut serial);
| --------- -^^^^^^^^^^
| | |
| | the trait `_embedded_hal_serial_Read<u8>` is not implemented for `&mut impl Write<u8> + Read<u8>`
| | help: consider removing the leading `&`-reference
| required by a bound introduced by this call
|
note: required by a bound in `lcd::read_char`
--> src\lcd.rs:6:32
|
6 | fn read_char(serial: &mut impl Read<u8>) -> char {
| ^^^^^^^^ required by this bound in `lcd::read_char`
For more information about this error, try `rustc --explain E0277`.
There is a version mismatch 
That is actually a good point
It seems that the only issue is the fact that we need to use the serial ports in this functions
In main it's declared
And just used
Here a function is created so new scope
And we need serial
Now the thing is, why the hell is that hard
Also we aren't using it directly
We are using it via read_char
Which it's fine and it works on it's own
wait, remove the &mut from those 2 read_char lines