1
+ /*
2
+ * Copyright (c) 2024, Henrik Hose
3
+ *
4
+ * This file is part of the modm project.
5
+ *
6
+ * This Source Code Form is subject to the terms of the Mozilla Public
7
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
8
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9
+ */
10
+
11
+ #include < modm/board.hpp>
12
+ #include < modm/debug/logger.hpp>
13
+ #include < nlohmann-json/json.hpp>
14
+
15
+ #undef MODM_LOG_LEVEL
16
+ #define MODM_LOG_LEVEL modm::log::INFO
17
+
18
+ // ----------------------------------------------------------------------------
19
+
20
+ extern " C" const uint32_t __flash_reserved_start[];
21
+
22
+ using json = nlohmann::json;
23
+
24
+ constexpr auto max_flash_pages{256 };
25
+
26
+ namespace data
27
+ {
28
+ struct person
29
+ {
30
+ std::string name;
31
+ float height;
32
+ int age;
33
+ auto
34
+ operator <=>(const person&) const = default ;
35
+ };
36
+
37
+ void
38
+ to_json (json& j, const person& p)
39
+ {
40
+ j = json{{" name" , p.name }, {" height" , p.height }, {" age" , p.age }};
41
+ }
42
+
43
+ void
44
+ from_json (const json& j, person& p)
45
+ {
46
+ j.at (" name" ).get_to (p.name );
47
+ j.at (" height" ).get_to (p.height );
48
+ j.at (" age" ).get_to (p.age );
49
+ }
50
+ } // namespace data
51
+
52
+ int
53
+ main ()
54
+ {
55
+ Board::initialize ();
56
+
57
+ MODM_LOG_INFO << " \n\n +++++++++++++++++++++++++++" << modm::endl;
58
+ MODM_LOG_INFO << " ++ NLOHMANN JSON example ++" << modm::endl;
59
+ MODM_LOG_INFO << " +++++++++++++++++++++++++++\n " << modm::endl;
60
+
61
+ data::person alice_struct = {" Alice" , 1.85 , 80 };
62
+ json alice_json = alice_struct;
63
+
64
+ // basic JSON character string usage
65
+ {
66
+ auto alice_json_string = alice_json.dump ();
67
+
68
+ // imagine that we transmit this string over some interface
69
+ MODM_LOG_INFO << " JSON from struct is: " << alice_json_string.c_str () << modm::endl;
70
+
71
+ // and now we can retrieve the struct from the json object
72
+ json retrieved_alice_json = json::parse (alice_json_string);
73
+
74
+ // JSON is fairly self-annotated, so we might not even need the original struct
75
+ MODM_LOG_INFO << " \n Retrieved from string JSON has fields ... " ;
76
+ for (auto & [key, value] : retrieved_alice_json.items ())
77
+ {
78
+ MODM_LOG_INFO << key.c_str () << " , " ;
79
+ }
80
+ MODM_LOG_INFO << modm::endl;
81
+ MODM_LOG_INFO.flush ();
82
+
83
+ // check if retrieved struct is same
84
+ auto retrieved_alice_struct = retrieved_alice_json.template get <data::person>();
85
+ if (retrieved_alice_struct == alice_struct)
86
+ {
87
+ MODM_LOG_INFO << " \n Retrieved from string and original struct are identical"
88
+ << modm::endl;
89
+ }
90
+ MODM_LOG_INFO.flush ();
91
+ }
92
+
93
+ // basic binary JSON usage
94
+ {
95
+ // convert to BSON
96
+ auto alice_binary = json::to_bson (alice_json);
97
+
98
+ MODM_LOG_INFO << " \n Binary JSON (BSON): " ;
99
+ for (const auto & byte : alice_binary) { MODM_LOG_INFO.printf (" 0x%02x" , byte); }
100
+ MODM_LOG_INFO << modm::endl;
101
+
102
+ // put BSON in reserved flash
103
+ uint32_t err{0 };
104
+ const size_t page_start =
105
+ Flash::getPage (reinterpret_cast <uint32_t >(&__flash_reserved_start));
106
+ const size_t num_bytes = alice_binary.size ();
107
+ const size_t flash_page_size = Flash::getSize (page_start);
108
+ const size_t end_page = page_start + (num_bytes + flash_page_size - 1 ) / flash_page_size;
109
+ if (end_page >= max_flash_pages)
110
+ {
111
+ MODM_LOG_ERROR << " \n Requested flash end page exceeds flash [" << page_start << " , "
112
+ << end_page << " )" << modm::endl;
113
+ MODM_LOG_ERROR.flush ();
114
+ while (1 )
115
+ ;
116
+ }
117
+
118
+ // erase the pages before programming
119
+ MODM_LOG_INFO << " \n Erasing flash sectors [" << page_start << " , " << end_page << " )"
120
+ << modm::endl;
121
+ MODM_LOG_INFO.flush ();
122
+ if (not Flash::unlock ())
123
+ {
124
+ MODM_LOG_ERROR << " Flash unlock failed!" << modm::endl;
125
+ while (1 )
126
+ ;
127
+ }
128
+ for (size_t page{page_start}; page < end_page; page++) err |= Flash::erase (page);
129
+ if (err != 0 )
130
+ {
131
+ MODM_LOG_ERROR << " \n There was an error while erasing flash!" << modm::endl;
132
+ MODM_LOG_ERROR.flush ();
133
+ while (1 )
134
+ ;
135
+ }
136
+
137
+ // pad the data with zeros to fit flash words
138
+ size_t unpadded_size{alice_binary.size ()};
139
+ size_t word_size{sizeof (Flash::MaxWordType)};
140
+ if (auto padding = (word_size - unpadded_size % word_size) % word_size; padding > 0 )
141
+ {
142
+ alice_binary.resize (alice_binary.size () + padding, 0 );
143
+ }
144
+
145
+ // now, write the padded data
146
+ MODM_LOG_INFO << " \n Writing, word size: " << word_size
147
+ << " , num of bytes (payload): " << unpadded_size
148
+ << " , num of bytes (padded): " << alice_binary.size () << " ... " ;
149
+
150
+ const auto flash_write_base_addr{reinterpret_cast <uint32_t >(Flash::getAddr (page_start))};
151
+ for (size_t ii = 0 ; ii < alice_binary.size (); ii += sizeof (Flash::MaxWordType))
152
+ {
153
+ Flash::MaxWordType outdata;
154
+ memcpy (&outdata, &alice_binary[ii], sizeof (Flash::MaxWordType));
155
+ err |= Flash::program (flash_write_base_addr + ii, outdata);
156
+ }
157
+
158
+ if (err != 0 )
159
+ {
160
+ MODM_LOG_ERROR << " \n There was an error while programming flash!" << modm::endl;
161
+ MODM_LOG_ERROR.flush ();
162
+ while (1 )
163
+ ;
164
+ }
165
+ MODM_LOG_INFO << " Writing complete! " << modm::endl;
166
+ MODM_LOG_INFO.flush ();
167
+
168
+ // we can now read BSON back from flash, first 4 bytes entry is the size
169
+ uint32_t read_len;
170
+ memcpy (&read_len, Flash::getAddr (page_start), sizeof (read_len));
171
+
172
+ // read the actual data
173
+ MODM_LOG_INFO << " \n Reading BSON of size " << read_len << " from flash: " ;
174
+ auto raw_bytes =
175
+ std::vector<uint8_t >(Flash::getAddr (page_start), Flash::getAddr (page_start) + read_len);
176
+ for (const auto & byte : raw_bytes) { MODM_LOG_INFO.printf (" 0x%02x" , byte); }
177
+ MODM_LOG_INFO << modm::endl;
178
+ MODM_LOG_INFO.flush ();
179
+
180
+ // reconstruct the data
181
+ auto retrieved_alice_json = json::from_bson (raw_bytes);
182
+ auto retrieved_alice_struct = retrieved_alice_json.template get <data::person>();
183
+ if (retrieved_alice_struct == alice_struct)
184
+ {
185
+ MODM_LOG_INFO << " \n Retrieved from flash and original struct are identical"
186
+ << modm::endl;
187
+ MODM_LOG_INFO.flush ();
188
+ }
189
+ }
190
+
191
+ while (1 )
192
+ ;
193
+ return 0 ;
194
+ }
0 commit comments