11/* *
22 **************************************************
33 *
4- * @file SetVCOM_Inkplate10.ino
5- * @brief WARNING! - VCOM voltage is written in EEPROM, which means it can be
6- * set a limited number of times, so don't run this sketch repeatedly!
7- * VCOM should be set once and then left as is.
8- *
9- * This example:
10- * - Reads the current VCOM value from the panel
11- * - Shows it on screen with a grayscale test pattern (LVGL)
12- * - Lets you type a new VCOM value over Serial ([-5.00, 0.00] V)
13- * - Programs it into the panel EEPROM (and stores it in ESP32 EEPROM too)
4+ * @file SetVCOM.ino
5+ * @brief WARNING! - VCOM voltage is written in EEPROM, which means it can be set a limited number of times,
6+ * so don't run this sketch repeateadly! VCOM should be set once and then left as is.
147 *
158 * For info on how to quickly get started with Inkplate 10 visit:
169 * https://soldered.com/documentation/inkplate/10/overview/
1710 *
18- * WARNING: Each VCOM write wears panel EEPROM. Use sparingly.
19- *
2011 * @authors Soldered
2112 * @date November 2025
2213 ***************************************************/
2314
24- #if !defined(ARDUINO_INKPLATE10) && !defined(ARDUINO_INKPLATE10V2)
25- #error "Wrong board selection for this example, please select e-radionica Inkplate10 or Soldered Inkplate10 in the boards menu."
26- #endif
2715
2816#include < EEPROM.h>
2917#include < Inkplate-LVGL.h>
3018#include < Wire.h>
3119
32- // Inkplate 10 in 3-bit grayscale mode
33- Inkplate inkplate (INKPLATE_3BIT);
20+ Inkplate inkplate (INKPLATE_3BIT); // Create an object on Inkplate library and also set the grayscale to 3bit.
3421
35- double currentVCOM; // Stores the current VCOM value read from panel
36- const int EEPROMAddress = 0 ; // Keep address 0 for correct VCOM storage in ESP32 EEPROM
22+ double currentVCOM; // Stores the current VCOM value stored on EEPROM
23+ const int EEPROMAddress= 0 ; // Should leave the address as it is for correct EEPROM reading later
3724double vcomVoltage;
3825
39- // Forward declarations
4026double readPanelVCOM ();
4127double getVCOMFromSerial (double *_vcom);
4228uint8_t writeVCOMToEEPROM (double v);
@@ -46,214 +32,171 @@ void displayTestImage();
4632void writeReg (uint8_t reg, float data);
4733uint8_t readReg (uint8_t reg);
4834
49- void setup ()
35+ void setup ()
5036{
51- Serial.begin (115200 ); // Start serial at 115200 baud
52- EEPROM.begin (512 ); // Initialize ESP32 EEPROM
53- Wire.begin (); // Initialize I2C bus
54- inkplate.begin (); // Initialize Inkplate + LVGL backend
55-
56- Serial.println (" The optimal VCOM Voltage for your Inkplate's panel can sometimes" );
57- Serial.println (" be found written on the flat cable connector." );
58- Serial.println (" Write VCOM voltage from epaper panel." );
59- Serial.println (" Don't forget negative (-) sign!" );
60- Serial.println (" Use dot as the decimal point." );
61- Serial.println (" For example: -1.23" );
62- Serial.println ();
63-
64- // First screen: current VCOM + grayscale bars
65- displayTestImage ();
37+ Serial.begin (115200 ); // Start serial at 115200 baud
38+ EEPROM.begin (512 ); // Initialize EEPROM
39+ Wire.begin (); // Initialize I2C buss
40+ inkplate.begin (); // Initialize the Inkplate
41+ Serial.println (" The optimal VCOM Voltage for your Inkplate's panel can sometimes be found written on the flat cable connector" );
42+ Serial.println (" Write VCOM voltage from epaper panel. \r\n Don't forget negative (-) sign!\r\n Use dot as the decimal point.\r\n For example -1.23\n " );
43+ displayTestImage ();
6644}
6745
68- void loop ()
69- {
70- if (Serial.available ())
71- {
72- // Read VCOM from Serial until it's in range [-5.0, 0.0]
73- do
74- {
75- getVCOMFromSerial (&vcomVoltage);
76- Serial.println (vcomVoltage, 2 );
77-
78- if (vcomVoltage < -5.0 || vcomVoltage > 0.0 )
79- {
80- Serial.println (" VCOM out of range! [-5.0, 0.0]" );
81- }
82- } while (vcomVoltage < -5.0 || vcomVoltage > 0.0 );
83-
84- // Program the panel EEPROM
85- // Internal IO pin 6 is used as the TPS65186 VCOM programming status pin
86- inkplate.internalIO .pinMode (6 , INPUT_PULLUP);
87-
88- if (writeVCOMToEEPROM (vcomVoltage))
89- {
90- EEPROM.put (EEPROMAddress, vcomVoltage);
91- EEPROM.commit ();
92- }
93-
94- // Clear LVGL screen and redraw test image with updated VCOM value
95- lv_obj_clean (lv_scr_act ());
96- displayTestImage ();
46+ void loop ()
47+ {
48+ if (Serial.available ()){
49+ // Serial.println("Enter VCOM value, it must be [-5, 0]");
50+ do {
51+ getVCOMFromSerial (&vcomVoltage);
52+ Serial.println (vcomVoltage, 2 );
53+ if (vcomVoltage < -5.0 || vcomVoltage > 0.0 ){
54+ Serial.println (" VCOM out of range! [-5, 0]" );
55+ }
56+ }while (vcomVoltage <-5.0 || vcomVoltage > 0.0 );
57+
58+ // Program the panel EEPROM
59+ inkplate.internalIO .pinMode (6 , INPUT_PULLUP);
60+ if (writeVCOMToEEPROM (vcomVoltage)){
61+ EEPROM.put (EEPROMAddress, vcomVoltage);
62+ EEPROM.commit ();
9763 }
64+ lv_obj_clean (lv_scr_act ());
65+ displayTestImage ();
66+ }
9867}
9968
100- // ----- VCOM helpers -----
101-
10269double readPanelVCOM ()
10370{
104- delay (10 ); // Give TPS65186 time so registers respond
105- uint8_t vcomL = readReg (0x03 ); // Low 8 bits from register 0x03
106- uint8_t vcomH = readReg (0x04 ) & 0x01 ; // Mask bit 0 (MSB of raw )
107- delay (10 );
108- int raw = (vcomH << 8 ) | vcomL; // 0 .. 511
109- return -(raw / 100.0 ); // VCOM in volts (negative)
71+ delay (10 ); // Wake up TPS65186 so registers respond
72+ uint8_t vcomL= readReg (0x03 ); // Read low 8 bits from register 0x03
73+ uint8_t vcomH = readReg (0x04 ) & 0x01 ; // Read full byte, mask off all but bit 0 (MSB)
74+ delay (10 ); // Power down driver
75+ int raw= (vcomH << 8 ) | vcomL; // Value between 0 - 511
76+ return -(raw/ 100.0 );
11077}
11178
11279double getVCOMFromSerial (double *_vcom)
11380{
114- double vcom = 0 ;
115- char buff[32 ];
116- unsigned long start;
117-
118- // Wait for first char
119- while (!Serial.available ())
120- ;
121-
122- start = millis ();
123- int idx = 0 ;
124-
125- // Collect characters for up to 500 ms idle gap
126- while ((millis () - start) < 500 && idx < (int )sizeof (buff) - 1 )
127- {
128- if (Serial.available ())
129- {
130- char c = Serial.read ();
131- buff[idx++] = c;
132- start = millis ();
133- }
81+ double vcom=0 ;
82+ char buff[32 ];
83+ unsigned long start;
84+ while (!Serial.available ());
85+ start=millis ();
86+ int idx=0 ;
87+ while ((millis ()-start)<500 && idx<sizeof (buff)-1 ){
88+ if (Serial.available ()){
89+ char c=Serial.read ();
90+ buff[idx++]=c;
91+ start=millis ();
13492 }
135-
136- buff[idx] = ' \0 ' ;
137- sscanf (buff, " %lf" , &vcom);
138- *_vcom = vcom;
139- return vcom;
93+ }
94+ buff[idx]= ' \0 ' ;
95+ sscanf (buff, " %lf" , &vcom);
96+ *_vcom= vcom;
97+ return vcom;
14098}
14199
142100uint8_t writeVCOMToEEPROM (double v)
143101{
144- // Build a 9-bit raw value (0 - 511)
145- int raw = int (abs (v) * 100 ) & 0x1FF ;
146- uint8_t lsb = raw & 0xFF ;
147- uint8_t msb = (raw >> 8 ) & 0x01 ;
148-
149- // NOTE: With Inkplate-LVGL, power control is handled internally.
150- // We assume the TPS65186 is powered from inkplate.begin().
151-
152- writeReg (0x03 , lsb);
153- uint8_t r4 = readReg (0x04 ) & ~0x01 ;
154- writeReg (0x04 , r4 | msb);
155- // Set bit 6 to trigger EEPROM programming
156- writeReg (0x04 , (r4 | msb) | (1 << 6 ));
157-
158- // Wait for VCOM programming to finish
159- while (inkplate.internalIO .digitalRead (6 ))
160- {
161- delay (1 );
162- }
163-
164- // Clear interrupt flag and clean registers
165- readReg (0x07 ); // clear interrupt flag
166- writeReg (0x03 , 0 );
167- writeReg (0x04 , 0 );
168-
169- // We skip explicit einkOff/einkOn here because those are private
170- // in the LVGL Inkplate10 driver and the driver takes care of power.
171-
172- // Verify written value
173- uint8_t vL = readReg (0x03 );
174- uint8_t vH = readReg (0x04 ) & 0x01 ;
175- int check = (vH << 8 ) | vL;
176-
177- if (check != raw)
178- {
179- Serial.printf (" Verification failed: got %d, want %d\n " , check, raw);
180- return 0 ;
181- }
182-
183- Serial.println (" VCOM EEPROM PROGRAMMING OK" );
184- return 1 ;
102+ // Build a 9-bit raw value (0 - 511)
103+ int raw=int (abs (v)*100 )&0x1FF ;
104+ uint8_t lsb=raw & 0xFF ;
105+ uint8_t msb=(raw >> 8 )&0x01 ;
106+
107+ inkplate.einkOn ();
108+ delay (10 );
109+
110+ writeReg (0x03 , lsb);
111+ uint8_t r4=readReg (0x04 )&~0x01 ;
112+ writeReg (0x04 , r4 | msb);
113+ writeReg (0x04 , (r4 | msb) | (1 << 6 ));
114+ while ( inkplate.internalIO .digitalRead (6 ) ) {
115+ delay (1 );
116+ }
117+ readReg (0x07 ); // clear interrupt flag
118+ writeReg (0x03 , 0 );
119+ writeReg (0x04 , 0 );
120+ inkplate.einkOff (); // WAKEUP low
121+ delay (10 );
122+ inkplate.einkOn (); // WAKEUP high
123+ delay (10 );
124+ uint8_t vL = readReg (0x03 );
125+ uint8_t vH = readReg (0x04 ) & 0x01 ;
126+ int check = (vH << 8 ) | vL;
127+ if (check != raw) {
128+ Serial.printf (" Verification failed: got %d, want %d\n " , check, raw);
129+ return 0 ;
130+ }
131+ Serial.println (" VCOM EEPROM PROGRAMMING OK" );
132+ return 1 ;
185133}
186134
187- // ----- TPS65186 I2C access -----
188-
189135void writeReg (uint8_t reg, float data)
190136{
191- Wire.beginTransmission (0x48 );
192- Wire.write (reg);
193- Wire.write ((uint8_t )data);
194- Wire.endTransmission ();
137+ Wire.beginTransmission (0x48 );
138+ Wire.write (reg);
139+ Wire.write ((uint8_t )data);
140+ Wire.endTransmission ();
195141}
196142
197143uint8_t readReg (uint8_t reg)
198144{
199- Wire.beginTransmission (0x48 );
200- Wire.write (reg);
201- Wire.endTransmission (false );
202- Wire.requestFrom ((uint8_t )0x48 , (uint8_t )1 );
203- return Wire.read ();
145+ Wire.beginTransmission (0x48 );
146+ Wire.write (reg);
147+ Wire.endTransmission (false );
148+ Wire.requestFrom ((uint8_t )0x48 , (uint8_t )1 );
149+ return Wire.read ();
204150}
205151
206- // ----- LVGL helpers -----
207-
208152static inline int display_width ()
209153{
210- lv_display_t *disp = lv_disp_get_default ();
211- return lv_display_get_horizontal_resolution (disp);
154+ lv_display_t *disp = lv_disp_get_default ();
155+ return lv_display_get_horizontal_resolution (disp);
212156}
213157
214158static inline int display_height ()
215159{
216- lv_display_t *disp = lv_disp_get_default ();
217- return lv_display_get_vertical_resolution (disp);
160+ lv_display_t *disp = lv_disp_get_default ();
161+ return lv_display_get_vertical_resolution (disp);
218162}
219163
220- void displayTestImage ()
164+ void displayTestImage ()
221165{
222- inkplate.clearDisplay ();
223- currentVCOM = readPanelVCOM ();
224-
225- // Top text: "Current VCOM: <value> V"
226- lv_obj_t *label = lv_label_create (lv_screen_active ());
227- lv_label_set_text (label, " Current VCOM: " );
228- lv_obj_set_style_text_color (lv_screen_active (), lv_color_hex (0x000000 ), LV_PART_MAIN);
229- lv_obj_set_style_text_font (label, &lv_font_montserrat_28, 0 );
230- lv_obj_align (label, LV_ALIGN_TOP_LEFT, 5 , 5 );
231-
232- lv_obj_t *label2 = lv_label_create (lv_scr_act ());
233- String vcomText = String (currentVCOM, 2 ) + " V" ;
234- lv_label_set_text (label2, vcomText.c_str ());
235- lv_obj_set_style_text_color (label2, lv_color_hex (0x000000 ), 0 );
236- lv_obj_set_style_text_font (label2, &lv_font_montserrat_28, 0 );
237- lv_obj_align_to (label2, label, LV_ALIGN_OUT_RIGHT_MID, 5 , 0 );
238-
239- // Grayscale gradient bars across the screen
240- for (int i = 0 ; i < 8 ; i++)
241- {
242- int x = (display_width () / 8 ) * i;
243-
244- uint8_t v = (i * 255 ) / 7 ;
245- lv_color_t color = lv_color_make (v, v, v);
246-
247- lv_obj_t *rect = lv_obj_create (lv_scr_act ());
248- lv_obj_set_size (rect, display_width () / 8 , display_height ());
249- lv_obj_set_pos (rect, x, 40 );
250- lv_obj_set_style_bg_color (rect, color, 0 );
251- lv_obj_set_style_border_width (rect, 0 , 0 );
252- lv_obj_set_style_radius (rect, 0 , 0 );
253- }
254-
255- // Push LVGL buffer to the e-paper
256- lv_tick_inc (50 );
257- lv_timer_handler ();
258- inkplate.display ();
259- }
166+ inkplate.clearDisplay ();
167+ currentVCOM = readPanelVCOM ();
168+
169+ /* Show text on the screen */
170+ lv_obj_t *label = lv_label_create (lv_screen_active ());
171+ lv_label_set_text (label, " Current VCOM: " );
172+ lv_obj_set_style_text_color (lv_screen_active (), lv_color_hex (0x000000 ), LV_PART_MAIN);
173+ lv_obj_set_style_text_font (label, &lv_font_montserrat_28, 0 );
174+ lv_obj_align (label, LV_ALIGN_TOP_LEFT, 5 , 5 );
175+
176+ /* Display value */
177+ lv_obj_t *label2 = lv_label_create (lv_scr_act ());
178+ lv_label_set_text (label2, String (currentVCOM, 2 ).c_str ());
179+ lv_obj_set_style_text_color (label2, lv_color_hex (0x000000 ), 0 );
180+ lv_obj_set_style_text_font (label2, &lv_font_montserrat_28, 0 );
181+ // Position label2 just to the right of label1
182+ lv_obj_align_to (label2, label, LV_ALIGN_OUT_RIGHT_MID, 5 , 0 );
183+
184+ for (int i = 0 ; i < 8 ; i++)
185+ {
186+ int x = (display_width () / 8 ) * i;
187+
188+ uint8_t v = (i * 255 ) / 7 ;
189+ lv_color_t color = lv_color_make (v, v, v);
190+
191+ lv_obj_t *rect = lv_obj_create (lv_scr_act ());
192+ lv_obj_set_size (rect, display_width () / 8 , display_height ());
193+ lv_obj_set_pos (rect, x, 40 );
194+ lv_obj_set_style_bg_color (rect, color, 0 );
195+ lv_obj_set_style_border_width (rect, 0 , 0 ); // Remove 'padding'
196+ lv_obj_set_style_radius (rect, 0 , 0 ); // No round corners
197+ }
198+ // Update the display
199+ lv_tick_inc (50 );
200+ lv_timer_handler ();
201+ inkplate.display ();
202+ }
0 commit comments