66 * SPDX-License-Identifier: MPL-2.0
77 */
88
9- #include " Wire.h"
9+ #include " Wire.h"
1010
11- // Setting new_address to 0 means that the module will get back its original address
12- const uint8_t new_address = 0 ;
13-
14- uint8_t address;
15-
16- void setup () {
17- Wire1.begin ();
18- Serial.begin (115200 );
19- delay (1000 );
20- if (new_address != 0 && (new_address < 8 || new_address > 0x77 )) {
21- Serial.println (" Address outside valid range" );
22- while (1 );
23- }
24- // Search for devices and wait for user confirmation
25- for (int i = 8 ; i < 128 ; i++) {
26- Wire1.beginTransmission (i);
27- auto err = Wire1.endTransmission ();
28- if (err == 0 ) {
29- Serial.print (" Found device at " );
30- Serial.println (i);
31- address = i;
32- Serial.println (" Press 'c' to configure te new address" );
33- }
34- }
35- }
36-
37- String pinstrapToName (uint8_t pinstrap) {
38- switch (pinstrap) {
39- case 0x3C :
40- return " BUZZER" ;
41- case 0x7C :
42- return " BUTTONS" ;
43- case 0x76 :
44- case 0x74 :
45- return " ENCODER" ;
46- case 0x6C :
47- return " SMARTLEDS" ;
48- }
49- return " UNKNOWN" ;
50- }
51-
52- void loop () {
53- // put your main code here, to run repeatedly:
54- if (Serial.available ()) {
55- if (Serial.read () == ' c' ) {
56- Serial.print (" Assigning new address to " );
57- Serial.println (address);
58- uint8_t data[40 ] = { ' C' , ' F' , new_address * 2 };
59- Wire1.beginTransmission (address);
60- Wire1.write (data, 40 );
61- Wire1.endTransmission ();
62- delay (1000 );
63- Wire1.requestFrom (new_address, 1 );
64- Serial.println (" Device type " + pinstrapToName (Wire1.read ()) + " at new address " + String (new_address));
65- }
66- }
67- }
11+ struct DetectedModulino {
12+ uint8_t addr;
13+ String modulinoType;
14+ String pinstrap;
15+ String defaultAddr;
16+ };
17+
18+ #define MAX_DEVICES 16
19+ DetectedModulino rows[MAX_DEVICES];
20+ int numRows = 0 ;
21+
22+
23+ void setup () {
24+ Wire1.begin ();
25+ Serial.begin (115200 );
26+
27+ delay (600 );
28+ discoverDevices ();
29+ }
30+
31+ bool waitingInput = false ;
32+ void loop () {
33+ if (numRows == 0 ) return ;
34+ if (Serial.available () == 0 && waitingInput) return ;
35+
36+ if (Serial.available () > 0 ) {
37+ String hex1 = Serial.readStringUntil (' ' ); // Read until space (or other delimiter)
38+ String hex2 = Serial.readStringUntil (' \n ' ); // Read until newline
39+ Serial.println (" > " +hex1+" " +hex2); // Print what the user inserted.
40+
41+ int num1 = parseHex (hex1); // Parse the first hex number
42+ int num2 = parseHex (hex2); // Parse the second hex number
43+ if (num1 == -1 || num2 == -1 ) {
44+ Serial.println (" Error: Incomplete or invalid input. Please enter two hexadecimal numbers" );
45+ return ;
46+ }
47+
48+ bool success = updateI2cAddress (num1, num2);
49+ if (!success) return ; // If the update failed, skip discovery and messages, and wait for input again.
50+
51+ discoverDevices ();
52+ waitingInput = false ;
53+ }
54+
55+ Serial.println (" Enter the current address, space, and new address (ex. \" 0x20 0x30\" or \" 20 2A\" ):" );
56+ Serial.println (" - Enter \" <addr> 0\" to reset the device at <addr> to its default address." );
57+ Serial.println (" - Enter \" 0 0\" to reset all devices to the default address." );
58+ waitingInput = true ;
59+ }
60+
61+ // Updates the device at current address to new address. Supports broadcasting and setting default address (0).
62+ // Returns true if the update was successful, false otherwise.
63+ bool updateI2cAddress (int curAddress, int newAddress) {
64+ uint8_t data[40 ] = { ' C' , ' F' , newAddress * 2 };
65+ memset (data + 3 , 0 , sizeof (data) - 3 ); // Zero the rest of the buffer.
66+
67+ // Validate the current address, it must match a detected device.
68+ if (curAddress != 0 && !findRow (curAddress)) {
69+ Serial.println (" Error: current address 0x" +String (curAddress, HEX)+" not found in the devices list\n " );
70+ return false ;
71+ }
72+
73+ if (curAddress != 0 && isFixedAddrDevice (curAddress)) {
74+ Serial.println (" Error: address 0x" +String (curAddress, HEX)+" is a non configurable device\n " );
75+ return false ;
76+ }
77+
78+ // Validate the new address.
79+ if (newAddress != 0 && (newAddress < 8 || newAddress > 0x77 )) {
80+ Serial.println (" Error: new address 0x" +String (newAddress, HEX)+" must be from 0x08 to 0x77\n " );
81+ return false ;
82+ }
83+
84+ if (curAddress == 0 ) {
85+ Serial.print (" Updating all devices (broadcast 0x00) to 0x" + String (newAddress, HEX));
86+ } else {
87+ Serial.print (" Updating the device address from 0x" + String (curAddress, HEX) + " to 0x" + String (newAddress, HEX));
88+ }
89+ if (newAddress == 0 ) Serial.print (" (default address)" );
90+ Serial.print (" ..." );
91+
92+ Wire1.beginTransmission (curAddress);
93+ Wire1.write (data, 40 );
94+ Wire1.endTransmission ();
95+
96+ delay (500 );
97+
98+ if (newAddress == 0 ) {
99+ Serial.println (" done\n " );
100+ return true ;
101+ } else {
102+ Wire1.requestFrom (newAddress, 1 );
103+ if (Wire1.available ()) {
104+ Serial.println (" done\n " );
105+ return true ;
106+ }
107+ else {
108+ Serial.println (" error\n " );
109+ return false ;
110+ }
111+ }
112+ }
113+
114+ // Function to parse hex number (with or without 0x prefix)
115+ int parseHex (String hexStr) {
116+ hexStr.trim ();
117+
118+ if (hexStr.length () == 0 ) {
119+ return -1 ;
120+ }
121+
122+ if (hexStr.startsWith (" 0x" ) || hexStr.startsWith (" 0X" )) {
123+ hexStr = hexStr.substring (2 ); // Remove the "0x" prefix
124+ }
125+
126+ // Validate that the remaining string contains only valid hexadecimal characters (0-9, A-F, a-f)
127+ for (int i = 0 ; i < hexStr.length (); i++) {
128+ if (!isHexDigit (hexStr.charAt (i))) {
129+ return -1 ;
130+ }
131+ }
132+
133+ return strtol (hexStr.c_str (), NULL , 16 );
134+ }
135+
136+ bool isHexDigit (char c) {
137+ return ( (c >= ' 0' && c <= ' 9' ) || (c >= ' A' && c <= ' F' ) || (c >= ' a' && c <= ' f' ) );
138+ }
139+
140+ void discoverDevices () {
141+ char buffer[64 ];
142+ Serial.println (" ADDR\t MODULINO\t PINSTRAP\t DEFAULT ADDR" ); // Table heading.
143+
144+ numRows = 0 ;
145+
146+ // Discover all modulino devices connected to the I2C bus.
147+ for (int addr = 8 ; addr < 128 ; addr++) {
148+ Wire1.beginTransmission (addr);
149+ if (Wire1.endTransmission () != 0 ) continue ;
150+
151+ if (numRows >= MAX_DEVICES) {
152+ Serial.println (" Too many devices connected, maximum supported is" + String (MAX_DEVICES));
153+ return ;
154+ }
155+
156+ // Some addresses represent non configurable devices (no MCU on it). Handle them as a special case.
157+ if (isFixedAddrDevice (addr)) {
158+ snprintf (buffer, 64 , " 0x%02X (cannot change)" , addr);
159+ addRow (addr, fixedAddrToName (addr), " -" , String (buffer));
160+
161+ continue ; // Stop here, do not try to communicate with this device.
162+ }
163+
164+ {
165+ uint8_t pinstrap = 0 ; // Variable to store the pinstrap (device type)
166+ Wire1.beginTransmission (addr); // Begin I2C transmission to the current address
167+ Wire1.write (0x00 ); // Send a request to the device (assuming 0x00 is the register for device type)
168+ Wire1.endTransmission (); // End transmission
169+
170+ delay (50 ); // Delay to allow for the device to respond
171+
172+ Wire1.requestFrom (addr, 1 ); // Request 1 byte from the device at the current address
173+ if (Wire1.available ()) {
174+ pinstrap = Wire1.read (); // Read the device type (pinstrap)
175+ } else {
176+ // If an error happens in the range 0x78 to 0x7F, ignore it.
177+ if (addr >= 0x78 ) continue ;
178+ Serial.println (" Failed to read device type at address 0x" + String (addr, HEX));
179+ }
180+
181+ snprintf (buffer, 64 , " 0x%02X" , pinstrap);
182+ auto hexPinstrap = String (buffer);
183+
184+ snprintf (buffer, 64 , " 0x%02X" , pinstrap / 2 ); // Default address is half pinstrap.
185+ auto defaultAddr = String (buffer);
186+ if (addr != pinstrap / 2 ) defaultAddr += " *" ; // Mark devices with modified address.
187+
188+ addRow (addr, pinstrapToName (pinstrap), hexPinstrap, defaultAddr);
189+ }
190+ }
191+
192+ // Print the results.
193+ for (int i = 0 ; i < numRows; i++) {
194+ char buffer[16 ];
195+ snprintf (buffer, 16 , " 0x%02X" , rows[i].addr );
196+
197+ Serial.print (fixedWidth (buffer, 8 ));
198+ Serial.print (fixedWidth (rows[i].modulinoType , 16 ));
199+ Serial.print (fixedWidth (rows[i].pinstrap , 16 ));
200+ Serial.println (fixedWidth (rows[i].defaultAddr , 12 ));
201+ }
202+ }
203+
204+ void addRow (uint8_t address, String modulinoType, String pinstrap, String defaultAddr) {
205+ if (numRows >= MAX_DEVICES) return ;
206+
207+ rows[numRows].addr = address;
208+ rows[numRows].modulinoType = modulinoType;
209+ rows[numRows].pinstrap = pinstrap;
210+ rows[numRows].defaultAddr = defaultAddr;
211+ numRows++; // Increment the row counter
212+ }
213+
214+ bool findRow (uint8_t address) {
215+ for (int i = 0 ; i < numRows; i++) {
216+ if (rows[i].addr == address) return true ;
217+ }
218+ return false ;
219+ }
220+
221+
222+ // Function to add padding to the right to ensure each field has a fixed width
223+ String fixedWidth (String str, int width) {
224+ for (int i = str.length (); i < width; i++) str += ' ' ;
225+ return str;
226+ }
227+
228+ String pinstrapToName (uint8_t pinstrap) {
229+ switch (pinstrap) {
230+ case 0x3C :
231+ return " Buzzer" ;
232+ case 0x7C :
233+ return " Buttons" ;
234+ case 0x76 :
235+ case 0x74 :
236+ return " Encoder" ;
237+ case 0x6C :
238+ return " Smartleds" ;
239+ }
240+ return " UNKNOWN" ;
241+ }
242+
243+ String fixedAddrToName (uint8_t address) {
244+ switch (address) {
245+ case 0x29 :
246+ return " Distance" ;
247+ case 0x44 :
248+ return " Thermo" ;
249+ case 0x6A :
250+ case 0x6B :
251+ return " Movement" ;
252+ }
253+ return " UNKNOWN" ;
254+ }
255+
256+ bool isFixedAddrDevice (uint8_t addr) {
257+ // List of non-configurable devices, recognized by their fixed I2C address.
258+ const uint8_t fixedAddr[] = {0x29 , 0x44 , 0x6A , 0x6B };
259+
260+ for (int i = 0 ; i < sizeof (fixedAddr) / sizeof (fixedAddr[0 ]); i++) {
261+ if (addr == fixedAddr[i]) return true ;
262+ }
263+ return false ;
264+ }
265+
0 commit comments