Diskuze: Výpis textových položek z pole
V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.


DarkCoder:9.4.2022 14:10
Použij write() namísto print().
display.write(array[i]);
display.write(array[i]);
Nic na obrazovku nevypíše, jen bílá obrazovka a černý čtverec
display.write(*array[i]);
Vypíše jen první znak
Problém je asi v tom, že funkce display.print má typ const char (hvězdička)str a já mám texty uložené v const char(hvězdička) array[]
DarkCoder:9.4.2022 21:11
To zda máš řetězce uložené v poli nebo ne je jedno.
deklarace parametru
const char *str
jasně říká, že argument funkce je ukazatel na konstantní řetězec, tedy řetězec, který se nebude měnit. Což ty ani měnit nemůžeš, jelikož řetězce jsou uloženy v tabulce řetězců a ty znáš pouze jejich adresy.
Spíše to vypadá, že knihovna kterou používáš není schopna pracovat s řetězci.
Použij místo print() funkci println().
DarkCoder:9.4.2022 22:24
4 znaky to vypíše proto, že prvkem pole je ukazatel a ten má velikost 4 byty. Pro určení délky řetězce použij funkci strlen() z knihovny string.h.
Př.
#include <stdio.h>
#include <string.h>
int main(void) {
char* foods[] = {
"Sunka Bohemia",
"Cesn syrova pomazanka",
"Hermelin",
"Smetana",
"Pazitka",
"Hovezi bujon tekuty"
};
size_t len = sizeof(foods) / sizeof(foods[0]);
for (size_t i = 0; i < len; i++) printf("%zu %s\n", strlen(foods[i]), foods[i]);
return 0;
}
Tedy ve tvém případě by to mohlo vypadat takto:
display.write(array[i], strlen(array[i]));
Což by značilo, že write() je přetížená metoda, kde jeden prototyp by mohl mít dva parametry. Kde jako první argument se předává ukazatel (ve tvém případě ukazatel na začátek pole) a druhý argument je počet vypisovaných znaků (počet znaků tedy délku řetězce získáš pomocí již zmiňované funkce strlen()). Někde ale musíš přecházet na nový řádek, aby si zobrazil data na novém řádku displeje.
Ještě by mělo fungovat:
int n = 0;
while (*(array[i]+n) != 0x00){
display.write(*array[i]+n);
n++;
}
Jen přesně nevím, jak mám načíst byte z paměti aktuálního textu pomocí pointru a zjistit, zda již nejsem na konci (0x00)
Když vyhodím cyklus, nechám jen write a zadám např. n = 3, správně se vypisují 4 písmena textu v array.
DarkCoder:9.4.2022 22:52
Ano, toto bude fungovat rovněž. Je to způsob, kde vypisuješ postupně jednotlivé znaky dokud nenarazíš na nul.
Vztah mezi indexací a ukazatelovou aritmetikou je:
a[i] ~ *(a+i)
DarkCoder:9.4.2022 23:01
Takto vypíšeš celé pole po jednotlivých znacích pomocí ukazatelové aritemtiky:
for (size_t i = 0; i < len; i++) {
for (char* p = foods[i]; *p; p++) putchar(*p);
putchar('\n');
}
Petan:10.4.2022 9:42
Prvni priklad musi fungovat,ale co je p vfor (int i = p; i < 6; i++) { musi tam byt for (int i = 0; i < 6; i++) {
Nejschůdnější asi bude použít:
display.write(array[i], sizeof(array[i]));
což ale vypíše jen 4 první znaky.
int x = strlen(array[i]);
display.write(array[i], strlen(array[i]));
Serial1.printf("%d", x);
nefunguje, udělá to jen bílý a černý displej viz obrázek výše. Na sériové lince 1 je ale správně vypsána délka "Cesn syrova pomazanka" 21 znaků viz obrázek z logického analyzátoru. Problém je asi ve funkci print, která očekává jiný typ dat.
Příkaz display.write(array[i], strlen(array[i])); je definován pomocí:
using write
// In Adafruit_GFX
public: using Print::write
v souboru Adafruit_GFX.h je pak:
using Print::write;
#if ARDUINO >= 100
virtual size_t write(uint8_t);
#else
virtual void write(uint8_t);
#endif
a v Print.h pak:
#ifndef Print_h
#define Print_h
#include <inttypes.h>
#include <stdio.h> // for size_t
#include <stdarg.h> // for printf
#define PRINTF_BUF 80
#include "WString.h"
#include "Printable.h"
#define DEC 10
#define HEX 16
#define OCT 8
#define BIN 2
class Print
{
private:
int write_error;
size_t printNumber(unsigned long, uint8_t);
size_t printFloat(double, uint8_t);
protected:
void setWriteError(int err = 1) { write_error = err; }
public:
Print() : write_error(0) {}
int getWriteError() { return write_error; }
void clearWriteError() { setWriteError(0); }
virtual size_t write(uint8_t) = 0;
size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual size_t write(const uint8_t *buffer, size_t size);
size_t write(const char *buffer, size_t size) {
return write((const uint8_t *)buffer, size);
}
// default to zero, meaning "a single write may block"
// should be overriden by subclasses with buffering
virtual int availableForWrite() { return 0; }
size_t print(const __FlashStringHelper *);
size_t print(const String &);
size_t print(const char[]);
size_t print(char);
size_t print(unsigned char, int = DEC);
size_t print(int, int = DEC);
size_t print(unsigned int, int = DEC);
size_t print(long, int = DEC);
size_t print(unsigned long, int = DEC);
size_t print(double, int = 2);
size_t print(const Printable&);
size_t println(const __FlashStringHelper *);
size_t println(const String &s);
size_t println(const char[]);
size_t println(char);
size_t println(unsigned char, int = DEC);
size_t println(int, int = DEC);
size_t println(unsigned int, int = DEC);
size_t println(long, int = DEC);
size_t println(unsigned long, int = DEC);
size_t println(double, int = 2);
size_t println(const Printable&);
size_t println(void);
size_t printf(const char[], ...);
virtual void flush() { /* Empty implementation for backward compatibility */ }
};
#endif
display.printf("%2d", dny[i]);
funguje správně. Funkce je definována v Print.cpp:
size_t Print::printf(const char format[], ...)
{
size_t r;
char buf[PRINTF_BUF];
va_list ap;
va_start(ap, format);
r = vsnprintf(buf, sizeof(buf), format, ap);
write(buf);
va_end(ap);
return r;
}
Celé Print.cpp je tady (možná by šlo použít příkaz pro tisk textu jídla funkci odtud:
#include <math.h>
#include "Arduino.h"
#include "Print.h"
// Public Methods //////////////////////////////////////////////////////////////
/* default implementation: may be overridden */
size_t Print::write(const uint8_t *buffer, size_t size)
{
size_t n = 0;
while (size--) {
if (write(*buffer++)) n++;
else break;
}
return n;
}
size_t Print::print(const __FlashStringHelper *ifsh)
{
return print(reinterpret_cast<const char *>(ifsh));
}
size_t Print::print(const String &s)
{
return write(s.c_str(), s.length());
}
size_t Print::print(const char str[])
{
return write(str);
}
size_t Print::print(char c)
{
return write(c);
}
size_t Print::print(unsigned char b, int base)
{
return print((unsigned long) b, base);
}
size_t Print::print(int n, int base)
{
return print((long) n, base);
}
size_t Print::print(unsigned int n, int base)
{
return print((unsigned long) n, base);
}
size_t Print::print(long n, int base)
{
if (base == 0) {
return write(n);
} else if (base == 10) {
if (n < 0) {
int t = print('-');
n = -n;
return printNumber(n, 10) + t;
}
return printNumber(n, 10);
} else {
return printNumber(n, base);
}
}
size_t Print::print(unsigned long n, int base)
{
if (base == 0) return write(n);
else return printNumber(n, base);
}
size_t Print::print(double n, int digits)
{
return printFloat(n, digits);
}
size_t Print::println(const __FlashStringHelper *ifsh)
{
size_t n = print(ifsh);
n += println();
return n;
}
size_t Print::print(const Printable& x)
{
return x.printTo(*this);
}
size_t Print::println(void)
{
return write("\r\n");
}
size_t Print::println(const String &s)
{
size_t n = print(s);
n += println();
return n;
}
size_t Print::println(const char c[])
{
size_t n = print(c);
n += println();
return n;
}
size_t Print::println(char c)
{
size_t n = print(c);
n += println();
return n;
}
size_t Print::println(unsigned char b, int base)
{
size_t n = print(b, base);
n += println();
return n;
}
size_t Print::println(int num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(unsigned int num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(long num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(unsigned long num, int base)
{
size_t n = print(num, base);
n += println();
return n;
}
size_t Print::println(double num, int digits)
{
size_t n = print(num, digits);
n += println();
return n;
}
size_t Print::println(const Printable& x)
{
size_t n = print(x);
n += println();
return n;
}
size_t Print::printf(const char format[], ...)
{
size_t r;
char buf[PRINTF_BUF];
va_list ap;
va_start(ap, format);
r = vsnprintf(buf, sizeof(buf), format, ap);
write(buf);
va_end(ap);
return r;
}
// Private Methods /////////////////////////////////////////////////////////////
size_t Print::printNumber(unsigned long n, uint8_t base)
{
char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
char *str = &buf[sizeof(buf) - 1];
*str = '\0';
// prevent crash if called with base == 1
if (base < 2) base = 10;
do {
char c = n % base;
n /= base;
*--str = c < 10 ? c + '0' : c + 'A' - 10;
} while(n);
return write(str);
}
size_t Print::printFloat(double number, uint8_t digits)
{
size_t n = 0;
if (isnan(number)) return print("nan");
if (isinf(number)) return print("inf");
if (number > 4294967040.0) return print ("ovf"); // constant determined empirically
if (number <-4294967040.0) return print ("ovf"); // constant determined empirically
// Handle negative numbers
if (number < 0.0)
{
n += print('-');
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for (uint8_t i=0; i<digits; ++i)
rounding /= 10.0;
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long)number;
double remainder = number - (double)int_part;
n += print(int_part);
// Print the decimal point, but only if there are digits beyond
if (digits > 0) {
n += print(".");
}
// Extract digits from the remainder one at a time
while (digits-- > 0)
{
remainder *= 10.0;
unsigned int toPrint = (unsigned int)remainder;
n += print(toPrint);
remainder -= toPrint;
}
return n;
}
DarkCoder:10.4.2022 21:25
Vypiš pole takto:
for (size_t i = 0; i < len; i++) {
for (char* p = foods[i]; *p; p++) display.write(*p);
display.write('\n');
}
Dal jsem to do stávající cyklu pro výpis položek pole, ale hlásí mi to chybu viz screenshot. Místo proměnné p, kterou už používám, jsem zvolit proměnnou "x".
for (char* x = array[i]; *x; x++) display.write(*x);
variable x
Type: char *
// In loop
char *x = <recovery - expr>(array[i])
Cannot initialize a variable of type 'char *' with an lvalue of type 'const char
*'clang(init_conversion_failed)
Při překladu program hlásí:
\Arduino\xiao_sleepRtc\xiao_sleepRtc.ino:108:29: error: invalid conversion
from 'const char*' to 'char*' [-fpermissive]
for (char* x = array[i]; *x; x++) display.write(*x);
pole array[i] je podtrženo
DarkCoder:10.4.2022 23:08
Přidej klíčové slovo const před deklaraci pointeru v inicializační části.
Caster:10.4.2022 23:57
for (const char* x = array[i]; *x; x++) {
display.print(*x);
}
In Print: public: size_t print(char)
netiskne
když ale použiji:
display.print("+");
vypíše všechny položky správně vč. jejich délky, jen jen samozřejmě
místo textu stále "+"
In Print: public: size_t print(const char[])
display.write(*x);
netiskne
In Adafruit_GFX: public: virtual size_t write(uint8_t)
display.write("+");
vypíše všechny položky správně vč. jejich délky, jen jen samozřejmě
místo textu stále "+"
In Adafruit_GFX: public: using Print::write
DarkCoder:11.4.2022 13:47
Arduino si zřejmě s ukazately moc netyká.. Tak jinak
Máme následující:
char* foods[] = {
"Sunka Bohemia",
"Cesn syrova pomazanka",
"Hermelin",
"Smetana",
"Pazitka",
"Hovezi bujon tekuty"
};
size_t len = sizeof(foods) / sizeof(foods[0]);
Zajímají mě výstupy u následujících úryvků kódu:
for (size_t i = 0; i < len; i++) {
display.setCursor(0, i);
display.print(strlen(foods[i]));
}
for (size_t i = 0, l = 0; i < len; i++) {
display.setCursor(0, i);
l = strlen(foods[i]);
display.print(l);
}
Očekávám 13 21 8 7 7 19, každé číslo na samostatném řádku
display.print(foods[2]);
display.println(foods[2]);
display.write(foods[2], 21);
display.write(foods[2], strlen(foods[2]));
size_t z = strlen(foods[2]);
display.write(foods[2], z);
Výsledky mě zajímají i na serial.
Co se uz nekdo nekdy ptal na audirino, tak rikal, ze pouziva nejakou vlastni
zjednodusenou verzi c++. Takze, zrada ciha uplne vsude a je treba najit kod pro
ten stroj
https://www.arduino.cc/…ypes/string/
char *myStrings[] = {"This is string 1", "This is string 2", "This is string 3",
"This is string 4", "This is string 5", "This is string 6"
};
void setup() {
Serial.begin(9600);
}
void loop() {
for (int i = 0; i < 6; i++) {
Serial.println(myStrings[i]);
delay(500);
}
}
char *foods[] = {
"Sunka Bohemia",
"Cesn syrova pomazanka",
"Hermelin",
"Smetana",
"Pazitka",
"Hovezi bujon tekuty"
};
void setup() {
Serial.begin(9600);
}
void loop() {
int i_start = 0
int i_end = 6
for (int i = i_start; i < i_end; i++) {
Serial.println(myStrings[i]);
delay(500);
}
}
Jinak, uz prvne jsem si toho vsiml, ale proc tam mas p?
for (int i = p; i < 6; i++) { // i = 0 ne?
for (int i = 0; i < 6; i++) {
DarkCoder:11.4.2022 14:59
Takze, zrada ciha uplne vsude a je treba najit kod pro ten stroj
Také jsem toho názoru podle toho co vidím 😀. Fórum plné podobných dotazů. Stále vznikají různé hybridy a pak to tak dopadá. C se drží na topu stále, je osvědčené a to už je tu 50 let! Vše nej k narozeninám!
Na tuto stránku jsem také narazil, na seriálu by neměl být problém, zajímají mě především výstupy na LCD. Tak uvidíme s Caster přijde.
DarkCoder:11.4.2022 15:04
To p tam představuje posun v poli. Záznamy, u kterých vypršela doba expirace zůstávají navrchu a aby se přeskočily, je třeba posouvat pozici na první vypisovány záznam. Již jsme to řešili v předchozí diskuzi. Samozřejmě tato verze je pouze provizorní, neboť záznamy zůstávají v poli a velmi rychle se to pole zaplní. Pro začátek to ale stačí.
@DarkCoder
for (size_t i = 0; i < len; i++) {
display.setCursor(0, Displ_Y);
display.print(strlen(food[i]));
Displ_Y = Displ_Y + 20;
}
Vypíše čísla 13 21 8 7 7 a 19 na samostatném řádku
for (size_t i = 0; i < len; i++) {
display.setCursor(0, Displ_Y);
l = strlen(foods[i]);
display.print(l);
Displ_Y = Displ_Y + 20;
}
Vypíše čísla 13 21 8 7 7 a 19 na samostatném řádku
display.print(foods[2]);
Vypíše na displej "Hermelin" dtt na seriovou linku
display.println(foods[2]);
Vypíše na displej "Hermelin" dtt na seriovou linku (vidím i CR, LF)
display.write(foods[2], 21);
Vypíše na displej v jednom řádku "Hermelin Smetana Pazi" dtto na seriovou linku
display.write(foods[2], strlen(foods[2]));
Vypíše na displej "Hermelin" dtto na seriovou linku
size_t z = strlen(foods[2]);
display.write(foods[2], z);
Vypíše na displej "Hermelin" dtto na seriovou linku
Obrázek z mého stolu, testování (Arduino XIAO, e-ink displej a logický
analyzátor LA104)
DarkCoder:11.4.2022 19:33
Skvělé! Výsledky přesně takové jaké jsem očekával.
Teď mě zajímá toto:
for (size_t i = 0, Displ_Y = 5; i < len; i++) {
display.setCursor(0, Displ_Y);
display.print(foods[i]));
Displ_Y += 20;
}
DarkCoder:11.4.2022 20:01
Popřípadě po zjednodušení:
for (size_t i = 0; i < len; i++) {
display.setCursor(0, 20 * i + 5);
display.print(foods[i]));
}
@DarkCoder
Super, fungují obě dvě verze. Problém byl asi tedy v tom, že jsem měl
proměnnou "i" dříve definovánu jako int. Pomohlo ji definovat jako size_t i
když přesně nevím, jako je v tom rozdíl od int.
Ve finále jsem tam přidal i tisk počtu dnů:
for (size_t i = 0; i < len; i++) {
display.setCursor(0, 20 * i + 5);
display.printf("%2d", dny[i]);
display.setCursor(36, 20 * i + 5);
display.print(foods[i]);
Displ_Y += 20; // Bude se používat pro vypisování expirovaných potravin, výpis musí začít odshora displeje
}
Fakt moc oceňuji skvělou pomoc DarkCodera, někdy zajdem na pivo .
P.S. Zjistil jsem, že má na funkčnost výpisu i vliv zadání x pozice začátku výpisu. Pro danou velikost písma to vypadá, že je písmo široké 5 + 1 (mezera) bod. Když jsem chtěl vypisovat foods od x pozice 18 (tj. za pevnou délkou čísla přidat 1 mezeru), text se překrýval, pro 32 se vůbec nic nezobrazilo (není násobek 6ti ?) a teď mi tam funguje 36 (násobek šířky písmena 6 * 6 ? mezera mezi počtem dnů a začátkem foods ale vůbec neodpovídá. To už je ale prkotina. Displej má rozlišení 296 x 128 bodů.
Obrázek výpisu jen foods a druhý včetně dnů do expirace
+5 Zkušeností

DarkCoder:11.4.2022 23:04
No vida, hned to vypadá veseleji když jsme se zdárně dopidili k výsledku. A barvy nebudou? Červená pro potraviny po expiraci.. 😀
V typu iterační proměnné to nebude, takže jaká kouzla si tam dělal víš jen ty. 😊 int je znaménková proměnná o velikosti 16 resp. 32 bitů. Size_t je beznamenková proměnná veliká minimálně 16 bitů a velká tak, aby bylo možné pracovat s objekty maximální velikosti. Bývá používaná právě jako iterační proměnná pro indexaci polí a je návratovou hodnotou funkce strlen() a operátoru sizeof. Ve 32b prostředí odpovídá typu unsigned long int a v 64b prostředí odpovídá unsigned long long int.
Jinak zdaleka ještě aplikace není hotová. Je třeba doplnit přidávání potravin, řazení, mazání záznamů po expiraci a mnoho dalšího. Ale jako základy jsou celkem dobré.
To nejpodstatnější je, že si se zase zase něčemu novému přiučil a mně se třeba jednou budou tvé zkušenosti s arduinem hodit. 😊
Pivo, to je nějaký nový datový typ? 😀
DarkCoder:11.4.2022 23:56
Když Ti to teď funguje, ověř mi ještě jednou tento způsob výpisu:
for (size_t i = 0; i < len; i++) {
display.setCursor(0, 20 * i + 5);
for (const char* prec = foods[i]; *prec; prec++) display.write(*prec);
}
Vyzkoušel a funguje také úplně v pohodě
Ve finální verzi programu mám RTC wakeup alarm každý den v 6:00, kdy se aktualizuje expirace potravin a displej, než přejde Arduino zpět do režimu spánku.
Červený výpis jídla pak:
if (dny[i] < 0) {
display.setTextColor(EPD_RED);
}
Zčásti mám již navržen plošný spoj na který přiletuji Arduino a posadím e-Ink displej. Pro napájení asi použiji malou knoflíkovou baterii Zinc-Air A675 a Boost Up měnič na 3.3V
3D krabičku navrhnu ve FreeCADu. Představu mám takovou, že nyní po nákupu sundám displej z lednice a texty do něj nahraju přes USB pomocí Arduino IDE. V druhé fázi dokončím program v Android studiu a texty se po jejich nadiktování do mobilu přenesou pomocí Bluetooth do Arduina, kam přidám BT modul.
DarkCoder:12.4.2022 10:58
Skvělé, že i způsob pomocí ukazatelové aritmetiky funguje. Použij verzi, jaká se ti
líbí..
Ve finální verzi tedy budou dvě jednotky (mobilní aplikace a arduino).
V arduinu bude uložen soubor s databází (potravina a její expirační doba), pojede si svým vlastním životem, kdy pouze v 6:00 ráno si provede aktualizaci expirace potravin, aktualizuje výpis na e-displeji a ponoří se do řežimu spánku. Zde je jedno vylepšení, expirační doba. Čas do expirace bylo lepší aby se počítalo tak, že arduino si při spuštění zjistí aktuální čas a provede rozdíly časů, čímž získá dobu, za jakou dojde u potraviny k vypršení expirační doby. Je to lepší řešení nežli odečet -1.
Vše ostatní, přidávání potraviny, ubírání potravin, třídění
databáze a další úkony, by bylo na straně mobilní aplikace. Z té pouze
pomocí bluetooth se přenese aktuální databázi do arduina. To znamená, že
pomocí mobilní aplikace je třeba po změně databáze probudit arduino,
nahrát novou databázi a znovu jej uspat. Ač je to už pouze ssw záležitost,
bude to možná ještě více zajímavější než operace na straně arduina.
Každopádně to celé, jak to popisuješ, vypadá skvěle a jsem zvědav na
hotovou verzi.
a veškeré úpravy databáze se budou provádět na straně mobilní aplikace (přidávání a ubírání potravin, třídění).
Caster:12.4.2022 11:57
Aktuální čas si program při nahrátí do Arduina načte z Windows. Přesnost RTC v MCU SAMD21G18 Arduino XIAO je v pohodě pro probuzení jednou za den.
myRtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
Budu se snažit, aby projekt vypadal profesionálně, stejně jako můj první, měření napětí autobaterie (ATtiny202 a Sigfox).
Zobrazeno 33 zpráv z 33.