├── LICENSE ├── README.md ├── esp32-self-triggered-irq-pwm-18KHz-potentiometer.ino ├── mosfet-pushpull-300Hz-2channel-pot.ino ├── mosfet-pushpull-300Hz.ino └── sin.xlsx /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp32-sine-inverter 2 | A variable voltage pure sinewave inverter using spwm capable of running at 50 or 60Hz 3 | This was meant to drive neon sign transformers but you can use it for low power mains AC loads 4 | 5 | Youtube video and schematic here: 6 | https://youtu.be/qDRr0Wr252A 7 | -------------------------------------------------------------------------------- /esp32-self-triggered-irq-pwm-18KHz-potentiometer.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file esp32-self-triggered-irq-pwm-18KHz-potentiometer.ino 3 | * @author cybernetic-research 4 | * @brief A simple open loop SPWM generator intended to be used to 5 | * generate ac at 50 or 60Hz. This program outputs SPWM at 18KHz for 6 | * 50hz, or 21600Hz for 60Hz. the PWM output is wired back into the 7 | * chip as a rising edge interrupt allowing a new PWM value to be 8 | * output every edge transition. An ADC is used to read a 9 | * potentiometer allowing the PWM output voltage to be raised or 10 | * lowered (because we scale the sine lookup table). 11 | * This program is meant to be used on ESP32 boards 12 | * @version 0.1 13 | * @date 2020-09-16 14 | * 15 | * @copyright Copyright (c) 2020 16 | * 17 | */ 18 | #include 19 | 20 | const byte led_gpio = 32; // the PWM pin the LED is attached to 21 | int32_t brightness = 0; // how bright the LED is 22 | const int potPin = 27; // Potentiometer is connected to GPIO 27 (Analog ADC1_CH6) 23 | int32_t potValue = 0; // variable for storing the potentiometer value 24 | 25 | 26 | uint8_t pwm=1; 27 | int32_t PWM_FEEDBACK_PIN = 18; 28 | 29 | #define MODULATING_FREQ 18000 // <-- 18KHz is 360 x 50 Hz for American 60Hz use number 21600 instead 30 | #define MAX_STEPS 359 // one pwm update per degree of mains sine 31 | 32 | //8-bit representation of a sinewave scaled to 0->255 33 | int32_t sinetable[]= 34 | { 35 | 127 , 36 | 129 , 37 | 131 , 38 | 134 , 39 | 136 , 40 | 138 , 41 | 140 , 42 | 143 , 43 | 145 , 44 | 147 , 45 | 149 , 46 | 151 , 47 | 154 , 48 | 156 , 49 | 158 , 50 | 160 , 51 | 162 , 52 | 164 , 53 | 166 , 54 | 169 , 55 | 171 , 56 | 173 , 57 | 175 , 58 | 177 , 59 | 179 , 60 | 181 , 61 | 183 , 62 | 185 , 63 | 187 , 64 | 189 , 65 | 191 , 66 | 193 , 67 | 195 , 68 | 196 , 69 | 198 , 70 | 200 , 71 | 202 , 72 | 204 , 73 | 205 , 74 | 207 , 75 | 209 , 76 | 211 , 77 | 212 , 78 | 214 , 79 | 216 , 80 | 217 , 81 | 219 , 82 | 220 , 83 | 222 , 84 | 223 , 85 | 225 , 86 | 226 , 87 | 227 , 88 | 229 , 89 | 230 , 90 | 231 , 91 | 233 , 92 | 234 , 93 | 235 , 94 | 236 , 95 | 237 , 96 | 239 , 97 | 240 , 98 | 241 , 99 | 242 , 100 | 243 , 101 | 243 , 102 | 244 , 103 | 245 , 104 | 246 , 105 | 247 , 106 | 248 , 107 | 248 , 108 | 249 , 109 | 250 , 110 | 250 , 111 | 251 , 112 | 251 , 113 | 252 , 114 | 252 , 115 | 253 , 116 | 253 , 117 | 253 , 118 | 254 , 119 | 254 , 120 | 254 , 121 | 254 , 122 | 254 , 123 | 254 , 124 | 254 , 125 | 255 , 126 | 254 , 127 | 254 , 128 | 254 , 129 | 254 , 130 | 254 , 131 | 254 , 132 | 254 , 133 | 253 , 134 | 253 , 135 | 253 , 136 | 252 , 137 | 252 , 138 | 251 , 139 | 251 , 140 | 250 , 141 | 250 , 142 | 249 , 143 | 248 , 144 | 248 , 145 | 247 , 146 | 246 , 147 | 245 , 148 | 244 , 149 | 243 , 150 | 243 , 151 | 242 , 152 | 241 , 153 | 240 , 154 | 239 , 155 | 237 , 156 | 236 , 157 | 235 , 158 | 234 , 159 | 233 , 160 | 231 , 161 | 230 , 162 | 229 , 163 | 227 , 164 | 226 , 165 | 225 , 166 | 223 , 167 | 222 , 168 | 220 , 169 | 219 , 170 | 217 , 171 | 216 , 172 | 214 , 173 | 212 , 174 | 211 , 175 | 209 , 176 | 207 , 177 | 205 , 178 | 204 , 179 | 202 , 180 | 200 , 181 | 198 , 182 | 196 , 183 | 195 , 184 | 193 , 185 | 191 , 186 | 189 , 187 | 187 , 188 | 185 , 189 | 183 , 190 | 181 , 191 | 179 , 192 | 177 , 193 | 175 , 194 | 173 , 195 | 171 , 196 | 169 , 197 | 166 , 198 | 164 , 199 | 162 , 200 | 160 , 201 | 158 , 202 | 156 , 203 | 154 , 204 | 151 , 205 | 149 , 206 | 147 , 207 | 145 , 208 | 143 , 209 | 140 , 210 | 138 , 211 | 136 , 212 | 134 , 213 | 131 , 214 | 129 , 215 | 127 , 216 | 125 , 217 | 123 , 218 | 120 , 219 | 118 , 220 | 116 , 221 | 114 , 222 | 111 , 223 | 109 , 224 | 107 , 225 | 105 , 226 | 103 , 227 | 100 , 228 | 98 , 229 | 96 , 230 | 94 , 231 | 92 , 232 | 90 , 233 | 88 , 234 | 85 , 235 | 83 , 236 | 81 , 237 | 79 , 238 | 77 , 239 | 75 , 240 | 73 , 241 | 71 , 242 | 69 , 243 | 67 , 244 | 65 , 245 | 63 , 246 | 61 , 247 | 59 , 248 | 58 , 249 | 56 , 250 | 54 , 251 | 52 , 252 | 50 , 253 | 49 , 254 | 47 , 255 | 45 , 256 | 43 , 257 | 42 , 258 | 40 , 259 | 38 , 260 | 37 , 261 | 35 , 262 | 34 , 263 | 32 , 264 | 31 , 265 | 29 , 266 | 28 , 267 | 27 , 268 | 25 , 269 | 24 , 270 | 23 , 271 | 21 , 272 | 20 , 273 | 19 , 274 | 18 , 275 | 17 , 276 | 15 , 277 | 14 , 278 | 13 , 279 | 12 , 280 | 11 , 281 | 11 , 282 | 10 , 283 | 9 , 284 | 8 , 285 | 7 , 286 | 6 , 287 | 6 , 288 | 5 , 289 | 4 , 290 | 4 , 291 | 3 , 292 | 3 , 293 | 2 , 294 | 2 , 295 | 1 , 296 | 1 , 297 | 1 , 298 | 0 , 299 | 0 , 300 | 0 , 301 | 0 , 302 | 0 , 303 | 0 , 304 | 0 , 305 | 0 , 306 | 0 , 307 | 0 , 308 | 0 , 309 | 0 , 310 | 0 , 311 | 0 , 312 | 0 , 313 | 1 , 314 | 1 , 315 | 1 , 316 | 2 , 317 | 2 , 318 | 3 , 319 | 3 , 320 | 4 , 321 | 4 , 322 | 5 , 323 | 6 , 324 | 6 , 325 | 7 , 326 | 8 , 327 | 9 , 328 | 10 , 329 | 11 , 330 | 11 , 331 | 12 , 332 | 13 , 333 | 14 , 334 | 15 , 335 | 17 , 336 | 18 , 337 | 19 , 338 | 20 , 339 | 21 , 340 | 23 , 341 | 24 , 342 | 25 , 343 | 27 , 344 | 28 , 345 | 29 , 346 | 31 , 347 | 32 , 348 | 34 , 349 | 35 , 350 | 37 , 351 | 38 , 352 | 40 , 353 | 42 , 354 | 43 , 355 | 45 , 356 | 47 , 357 | 49 , 358 | 50 , 359 | 52 , 360 | 54 , 361 | 56 , 362 | 58 , 363 | 59 , 364 | 61 , 365 | 63 , 366 | 65 , 367 | 67 , 368 | 69 , 369 | 71 , 370 | 73 , 371 | 75 , 372 | 77 , 373 | 79 , 374 | 81 , 375 | 83 , 376 | 85 , 377 | 88 , 378 | 90 , 379 | 92 , 380 | 94 , 381 | 96 , 382 | 98 , 383 | 100 , 384 | 103 , 385 | 105 , 386 | 107 , 387 | 109 , 388 | 111 , 389 | 114 , 390 | 116 , 391 | 118 , 392 | 120 , 393 | 123 , 394 | 125 , 395 | }; 396 | 397 | 398 | 399 | uint8_t readPot =0; 400 | int32_t ctr=0; 401 | 402 | /** 403 | * @brief IRAM_ATTR 404 | * An interrupt service routine that runs whenever a rising edge is detected on 405 | * the feedback I/O pin. We generate a PWM pulse at 18KHz for 50Hz, or 21.6Khz 406 | * for a 60Hz pulse. the PWM output pin is fed back into the chip as an 407 | * interrupt and we retrigger from it. 408 | * Every time an interrupt is received we load in the next PWM value from the 409 | * sine table. this allows each degree of the 50 or 60Hz waveform to have a 410 | * separate pulse associated with it 411 | */ 412 | void IRAM_ATTR isr() { 413 | int32_t val = 1; 414 | val = (sinetable[ctr] * potValue) / 255 ; //scale the sine tablke 415 | ctr+=1; 416 | if(ctr>MAX_STEPS) 417 | { 418 | ctr=0; 419 | readPot = 1; 420 | } 421 | if(val==0) 422 | { 423 | val = 1; 424 | } 425 | ledcWrite(0, val); // set the brightness of the LED 426 | 427 | } 428 | 429 | 430 | 431 | 432 | 433 | 434 | /** 435 | * @brief setup 436 | * the setup routine runs once when you press reset: 437 | */ 438 | void setup() { 439 | ledcAttachPin(led_gpio, 0); // assign a led pins to a channel 440 | 441 | // Initialize channels 442 | // channels 0-15, resolution 1-16 bits, freq limits depend on resolution 443 | // ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits); 444 | ledcSetup(0, MODULATING_FREQ, 8); // 18 kHz PWM, 8-bit resolution 445 | pinMode(PWM_FEEDBACK_PIN, INPUT_PULLDOWN); 446 | attachInterrupt(PWM_FEEDBACK_PIN, isr, RISING); //this is wired to the PWM output 447 | ledcWrite(0, pwm); //signal to mosfet gate 448 | Serial.begin(115200); 449 | } 450 | 451 | 452 | 453 | 454 | 455 | /** 456 | * @brief loop 457 | * Shall read ADC value and allow user to modify PWM amplitude by means of 458 | * potentiometer. We scale the SPWM in fact, allowing a synthesised lower voltage 459 | */ 460 | int32_t t = 0; 461 | void loop() { 462 | 463 | if(readPot) 464 | { 465 | readPot = 0; 466 | int p = analogRead(potPin); //https://randomnerdtutorials.com/esp32-adc-analog-read-arduino-ide/ 467 | t+=1; 468 | if(20==t) 469 | { 470 | Serial.printf("ADCM %u\n", potValue); 471 | t=0; 472 | } 473 | potValue = p/16; 474 | if(potValue<1) 475 | { 476 | potValue = 0; 477 | } 478 | if(potValue>255) 479 | { 480 | potValue = 255; 481 | } 482 | } 483 | } 484 | -------------------------------------------------------------------------------- /mosfet-pushpull-300Hz-2channel-pot.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file mosfet-pushpull-300Hz-2channel-pot.ino 3 | * @author cybernetic-research 4 | * @brief A simple open loop SPWM generator intended to be used to 5 | * generate ac at 50 or 60Hz. This program outputs SPWM at 18KHz for 6 | * 50hz, or 21600Hz for 60Hz. the PWM output is wired back into the 7 | * chip as a rising edge interrupt allowing a new PWM value to be 8 | * output every edge transition. An ADC is used to read a 9 | * potentiometer allowing the PWM output voltage to be raised or 10 | * lowered (because we scale the sine lookup table). 11 | * This program is meant to be used on ESP32 boards 12 | * 13 | * This version of the code is intended to run with 2 potentiometers 14 | * and drive two independant SPWM channels to a pair of inverters 15 | * 16 | * This version is for a push pull configuration with centre tapped 17 | * transformers 18 | * 19 | * @version 0.1 20 | * @date 2020-10-08 21 | * 22 | * @copyright Copyright (c) 2020 23 | * 24 | */ 25 | #include 26 | //pinouts here: https://www.bing.com/images/search?view=detailV2&ccid=L%2bau9S29&id=5019621DC7350272217DF7C36EE9AD869B1CE64D&thid=OIP.L-au9S29i6JYHjCKPQ5WzQHaEk&mediaurl=https%3a%2f%2flastminuteengineers.com%2fwp-content%2fuploads%2f2018%2f08%2fESP32-Development-Board-Pinout.jpg&exph=468&expw=758&q=esp32+arduino+pinout&simid=607997902785809138&ck=FCAD5061950FE300E30F4A7416EE3687&selectedIndex=1&FORM=IRPRST&ajaxhist=0 27 | 28 | int32_t brightness = 0; // how bright the LED is 29 | uint8_t pwm = 1; 30 | 31 | #define SELF_TRIGGERING_IRQ 0 32 | #define MAX_PWM 255 33 | #define MID_PWM 127 34 | const byte led_gpio = 32; // the PWM pin the LED is attached to 35 | 36 | const int DRIVE_PWM[4] = { 2, 3, 4, 5 }; 37 | const int PUSHPULL_DRIVE[4] = { 33, 25, 22, 23 }; 38 | const int potPin[4] = { 27, 14, 27, 27 }; // Potentiometer is connected to GPIO 27 (Analog ADC1_CH6) 39 | int potVal[4] = { 0, 0, 0, 0 }; 40 | int32_t PWM_FEEDBACK_PIN = 18; 41 | 42 | //8-bit representation of a sinewave scaled to 0->255 43 | 44 | #define SIN_6_STEP_300Hz 0 45 | #define SIN_8_STEP_400Hz 0 46 | #define SIN_360_STEP_18KHz 1 47 | 48 | 49 | //6 step 50 | #if SIN_6_STEP_300Hz 51 | #define MODULATING_FREQ 300 // 300Hz 52 | #define MAX_STEPS 6 // discrete signal steps 53 | #define TRANSISTOR_SWITCH_STEP 2 // when to use other transistor bank 54 | int32_t sinetable[]= 55 | { 56 | 0, 57 | 255, 58 | 255, 59 | 0, 60 | 255, 61 | 255, 62 | }; 63 | #endif 64 | 65 | 66 | 67 | //8 step 68 | #if SIN_8_STEP_400Hz 69 | #define MODULATING_FREQ 400 // <-- 18KHz is 360 x 50 Hz for American 60Hz use number 21600 instead 70 | #define MAX_STEPS 8 // one pwm update per degree of mains sine 71 | #define TRANSISTOR_SWITCH_STEP 3 // when to use other transistor bank 72 | int32_t sinetable[]= 73 | { 74 | 0, 75 | 180, 76 | 255, 77 | 180, 78 | 0, 79 | 180, 80 | 255, 81 | 180 82 | }; 83 | #endif 84 | 85 | 86 | 87 | //360 step 88 | #if SIN_360_STEP_18KHz 89 | #define MODULATING_FREQ 18000 // <-- 18KHz is 360 x 50 Hz for American 60Hz use number 21600 instead 90 | #define MAX_STEPS 360 // one pwm update per degree of mains sine 91 | #define TRANSISTOR_SWITCH_STEP 179 // when to use other transistor bank 92 | int32_t sinetable[]= 93 | { 94 | 0 , 95 | 4 , 96 | 8 , 97 | 13 , 98 | 17 , 99 | 22 , 100 | 26 , 101 | 31 , 102 | 35 , 103 | 39 , 104 | 44 , 105 | 48 , 106 | 53 , 107 | 57 , 108 | 61 , 109 | 65 , 110 | 70 , 111 | 74 , 112 | 78 , 113 | 83 , 114 | 87 , 115 | 91 , 116 | 95 , 117 | 99 , 118 | 103 , 119 | 107 , 120 | 111 , 121 | 115 , 122 | 119 , 123 | 123 , 124 | 127 , 125 | 131 , 126 | 135 , 127 | 138 , 128 | 142 , 129 | 146 , 130 | 149 , 131 | 153 , 132 | 156 , 133 | 160 , 134 | 163 , 135 | 167 , 136 | 170 , 137 | 173 , 138 | 177 , 139 | 180 , 140 | 183 , 141 | 186 , 142 | 189 , 143 | 192 , 144 | 195 , 145 | 198 , 146 | 200 , 147 | 203 , 148 | 206 , 149 | 208 , 150 | 211 , 151 | 213 , 152 | 216 , 153 | 218 , 154 | 220 , 155 | 223 , 156 | 225 , 157 | 227 , 158 | 229 , 159 | 231 , 160 | 232 , 161 | 234 , 162 | 236 , 163 | 238 , 164 | 239 , 165 | 241 , 166 | 242 , 167 | 243 , 168 | 245 , 169 | 246 , 170 | 247 , 171 | 248 , 172 | 249 , 173 | 250 , 174 | 251 , 175 | 251 , 176 | 252 , 177 | 253 , 178 | 253 , 179 | 254 , 180 | 254 , 181 | 254 , 182 | 254 , 183 | 254 , 184 | 255 , 185 | 254 , 186 | 254 , 187 | 254 , 188 | 254 , 189 | 254 , 190 | 253 , 191 | 253 , 192 | 252 , 193 | 251 , 194 | 251 , 195 | 250 , 196 | 249 , 197 | 248 , 198 | 247 , 199 | 246 , 200 | 245 , 201 | 243 , 202 | 242 , 203 | 241 , 204 | 239 , 205 | 238 , 206 | 236 , 207 | 234 , 208 | 232 , 209 | 231 , 210 | 229 , 211 | 227 , 212 | 225 , 213 | 223 , 214 | 220 , 215 | 218 , 216 | 216 , 217 | 213 , 218 | 211 , 219 | 208 , 220 | 206 , 221 | 203 , 222 | 200 , 223 | 198 , 224 | 195 , 225 | 192 , 226 | 189 , 227 | 186 , 228 | 183 , 229 | 180 , 230 | 177 , 231 | 173 , 232 | 170 , 233 | 167 , 234 | 163 , 235 | 160 , 236 | 156 , 237 | 153 , 238 | 149 , 239 | 146 , 240 | 142 , 241 | 138 , 242 | 135 , 243 | 131 , 244 | 127 , 245 | 123 , 246 | 119 , 247 | 115 , 248 | 111 , 249 | 107 , 250 | 103 , 251 | 99 , 252 | 95 , 253 | 91 , 254 | 87 , 255 | 83 , 256 | 78 , 257 | 74 , 258 | 70 , 259 | 65 , 260 | 61 , 261 | 57 , 262 | 53 , 263 | 48 , 264 | 44 , 265 | 39 , 266 | 35 , 267 | 31 , 268 | 26 , 269 | 22 , 270 | 17 , 271 | 13 , 272 | 8 , 273 | 4 , 274 | 0 , 275 | 5 , 276 | 9 , 277 | 14 , 278 | 18 , 279 | 23 , 280 | 27 , 281 | 32 , 282 | 36 , 283 | 40 , 284 | 45 , 285 | 49 , 286 | 54 , 287 | 58 , 288 | 62 , 289 | 66 , 290 | 71 , 291 | 75 , 292 | 79 , 293 | 84 , 294 | 88 , 295 | 92 , 296 | 96 , 297 | 100 , 298 | 104 , 299 | 108 , 300 | 112 , 301 | 116 , 302 | 120 , 303 | 124 , 304 | 128 , 305 | 132 , 306 | 136 , 307 | 139 , 308 | 143 , 309 | 147 , 310 | 150 , 311 | 154 , 312 | 157 , 313 | 161 , 314 | 164 , 315 | 168 , 316 | 171 , 317 | 174 , 318 | 178 , 319 | 181 , 320 | 184 , 321 | 187 , 322 | 190 , 323 | 193 , 324 | 196 , 325 | 199 , 326 | 201 , 327 | 204 , 328 | 207 , 329 | 209 , 330 | 212 , 331 | 214 , 332 | 217 , 333 | 219 , 334 | 221 , 335 | 224 , 336 | 226 , 337 | 228 , 338 | 230 , 339 | 232 , 340 | 233 , 341 | 235 , 342 | 237 , 343 | 239 , 344 | 240 , 345 | 242 , 346 | 243 , 347 | 244 , 348 | 246 , 349 | 247 , 350 | 248 , 351 | 249 , 352 | 250 , 353 | 251 , 354 | 252 , 355 | 252 , 356 | 253 , 357 | 254 , 358 | 254 , 359 | 255 , 360 | 255 , 361 | 255 , 362 | 255 , 363 | 255 , 364 | 255 , 365 | 255 , 366 | 255 , 367 | 255 , 368 | 255 , 369 | 255 , 370 | 254 , 371 | 254 , 372 | 253 , 373 | 252 , 374 | 252 , 375 | 251 , 376 | 250 , 377 | 249 , 378 | 248 , 379 | 247 , 380 | 246 , 381 | 244 , 382 | 243 , 383 | 242 , 384 | 240 , 385 | 239 , 386 | 237 , 387 | 235 , 388 | 233 , 389 | 232 , 390 | 230 , 391 | 228 , 392 | 226 , 393 | 224 , 394 | 221 , 395 | 219 , 396 | 217 , 397 | 214 , 398 | 212 , 399 | 209 , 400 | 207 , 401 | 204 , 402 | 201 , 403 | 199 , 404 | 196 , 405 | 193 , 406 | 190 , 407 | 187 , 408 | 184 , 409 | 181 , 410 | 178 , 411 | 174 , 412 | 171 , 413 | 168 , 414 | 164 , 415 | 161 , 416 | 157 , 417 | 154 , 418 | 150 , 419 | 147 , 420 | 143 , 421 | 139 , 422 | 136 , 423 | 132 , 424 | 128 , 425 | 124 , 426 | 120 , 427 | 116 , 428 | 112 , 429 | 108 , 430 | 104 , 431 | 100 , 432 | 96 , 433 | 92 , 434 | 88 , 435 | 84 , 436 | 79 , 437 | 75 , 438 | 71 , 439 | 66 , 440 | 62 , 441 | 58 , 442 | 54 , 443 | 49 , 444 | 45 , 445 | 40 , 446 | 36 , 447 | 32 , 448 | 27 , 449 | 23 , 450 | 18 , 451 | 14 , 452 | 9 , 453 | 5 , 454 | 1 , 455 | }; 456 | #endif 457 | 458 | 459 | 460 | int32_t readPot = 0; 461 | int32_t ctr=0; 462 | /** 463 | * @brief IRAM_ATTR 464 | * An interrupt service routine that runs whenever a rising edge is detected on 465 | * the feedback I/O pin. We generate a PWM pulse at 18KHz for 50Hz, or 21.6Khz 466 | * for a 60Hz pulse. the PWM output pin is fed back into the chip as an 467 | * interrupt and we retrigger from it. 468 | * Every time an interrupt is received we load in the next PWM value from the 469 | * sine table. this allows each degree of the 50 or 60Hz waveform to have a 470 | * separate pulse associated with it 471 | */ 472 | void IRAM_ATTR isr() 473 | { 474 | 475 | /// 476 | /// 1. Compute next PWM register value 477 | /// 478 | int32_t val[2]; 479 | val[0] = (sinetable[ctr] * potVal[0]) / MAX_PWM ; //scale the sine table 480 | val[1] = (sinetable[ctr] * potVal[1]) / MAX_PWM ; //scale the sine table 481 | 482 | 483 | /// 484 | /// 2. select which transitor bank to use 485 | /// 486 | if(ctr>TRANSISTOR_SWITCH_STEP) 487 | { 488 | ledcWrite(DRIVE_PWM[0], val[0]); //signal to mosfet gate 489 | ledcWrite(DRIVE_PWM[1], 0); //signal to mosfet gate 490 | // 491 | ledcWrite(DRIVE_PWM[2], val[1]); //signal to mosfet gate 492 | ledcWrite(DRIVE_PWM[3], 0); //signal to mosfet gate 493 | } 494 | else 495 | { 496 | ledcWrite(DRIVE_PWM[0], 0); //signal to mosfet gate 497 | ledcWrite(DRIVE_PWM[1], val[0]); //signal to mosfet gate 498 | // 499 | ledcWrite(DRIVE_PWM[2], 0); //signal to mosfet gate 500 | ledcWrite(DRIVE_PWM[3], val[1]); //signal to mosfet gate 501 | } 502 | 503 | 504 | /// 505 | /// 3. increment counter 506 | /// 507 | ctr += 1; 508 | if(ctr==MAX_STEPS) 509 | { 510 | ctr = 0; 511 | readPot = 1; //allow amplitude to change 512 | } 513 | 514 | 515 | /// 516 | /// 4. self triggering interrupt (mandatory, it just has to have some non-zero value) 517 | /// 518 | ledcWrite(SELF_TRIGGERING_IRQ, 1); // set the brightness of the LED 519 | 520 | } 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | /** 531 | * @brief setup 532 | * the setup routine runs once when you press reset: 533 | */ 534 | void setup() { 535 | 536 | 537 | // Initialize channels 538 | // channels 0-15, resolution 1-16 bits, freq limits depend on resolution 539 | // ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits); 540 | ledcSetup(SELF_TRIGGERING_IRQ, MODULATING_FREQ, 8); // 18 kHz PWM, 8-bit resolution 541 | ledcSetup(DRIVE_PWM[0], MODULATING_FREQ, 8); // 18 kHz PWM, 8-bit resolution 542 | ledcSetup(DRIVE_PWM[1], MODULATING_FREQ, 8); // 18 kHz PWM, 8-bit resolution 543 | ledcSetup(DRIVE_PWM[2], MODULATING_FREQ, 8); // 18 kHz PWM, 8-bit resolution 544 | ledcSetup(DRIVE_PWM[3], MODULATING_FREQ, 8); // 18 kHz PWM, 8-bit resolution 545 | 546 | ledcAttachPin(led_gpio, SELF_TRIGGERING_IRQ); // assign a led pins to a channel 547 | ledcAttachPin(PUSHPULL_DRIVE[0], DRIVE_PWM[0]); 548 | ledcAttachPin(PUSHPULL_DRIVE[1], DRIVE_PWM[1]); 549 | ledcAttachPin(PUSHPULL_DRIVE[2], DRIVE_PWM[2]); 550 | ledcAttachPin(PUSHPULL_DRIVE[3], DRIVE_PWM[3]); 551 | 552 | 553 | pinMode(PWM_FEEDBACK_PIN, INPUT_PULLDOWN); 554 | attachInterrupt(PWM_FEEDBACK_PIN, isr, RISING); //this is wired to the PWM output 555 | 556 | ledcWrite(SELF_TRIGGERING_IRQ, pwm); //signal to mosfet gate 557 | ledcWrite(DRIVE_PWM[0], MID_PWM); //signal to mosfet gate 558 | ledcWrite(DRIVE_PWM[1], MID_PWM); //signal to mosfet gate 559 | ledcWrite(DRIVE_PWM[2], MID_PWM); //signal to mosfet gate 560 | ledcWrite(DRIVE_PWM[3], MID_PWM); //signal to mosfet gate 561 | Serial.begin(115200); 562 | 563 | } 564 | 565 | 566 | 567 | /** 568 | * @brief loop 569 | * Shall read ADC value and allow user to modify PWM amplitude by means of 570 | * potentiometer. We scale the SPWM in fact, allowing a synthesised lower voltage 571 | */ 572 | void loop() { 573 | //readPot = 1; 574 | if(readPot) 575 | { 576 | readPot = 0; 577 | 578 | potVal[0] = analogRead(potPin[0])/16; //scale to 255 range //https://randomnerdtutorials.com/esp32-adc-analog-read-arduino-ide/ 579 | potVal[1] = analogRead(potPin[1])/16; //scale to 255 range 580 | Serial.printf("ADC,[0]= %u, [1]=%u\n", potVal[0],potVal[1]); 581 | } 582 | } 583 | -------------------------------------------------------------------------------- /mosfet-pushpull-300Hz.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * @file esp32-self-triggered-irq-pwm-18KHz-potentiometer-pushpull.ino 3 | * @author cybernetic-research 4 | * @brief A simple open loop SPWM generator intended to be used to 5 | * generate ac at 50 or 60Hz. This program outputs SPWM at 18KHz for 6 | * 50hz, or 21600Hz for 60Hz. the PWM output is wired back into the 7 | * chip as a rising edge interrupt allowing a new PWM value to be 8 | * output every edge transition. An ADC is used to read a 9 | * potentiometer allowing the PWM output voltage to be raised or 10 | * lowered (because we scale the sine lookup table). 11 | * This program is meant to be used on ESP32 boards 12 | * 13 | * This version is for a push pull configuration with centre tapped 14 | * transformers 15 | * 16 | * @version 0.1 17 | * @date 2020-09-19 18 | * 19 | * @copyright Copyright (c) 2020 20 | * 21 | */ 22 | #include 23 | //pinouts here: https://www.bing.com/images/search?view=detailV2&ccid=L%2bau9S29&id=5019621DC7350272217DF7C36EE9AD869B1CE64D&thid=OIP.L-au9S29i6JYHjCKPQ5WzQHaEk&mediaurl=https%3a%2f%2flastminuteengineers.com%2fwp-content%2fuploads%2f2018%2f08%2fESP32-Development-Board-Pinout.jpg&exph=468&expw=758&q=esp32+arduino+pinout&simid=607997902785809138&ck=FCAD5061950FE300E30F4A7416EE3687&selectedIndex=1&FORM=IRPRST&ajaxhist=0 24 | 25 | int32_t potValue = 0; // variable for storing the potentiometer value 26 | int32_t brightness = 0; // how bright the LED is 27 | uint8_t pwm = 1; 28 | 29 | #define PUSHPULL_DRIVE_1 33 30 | #define PUSHPULL_DRIVE_2 25 31 | #define SELF_TRIGGERING_IRQ 0 32 | #define DRIVE1_PWM 1 33 | #define DRIVE2_PWM 2 34 | 35 | const byte led_gpio = 32; // the PWM pin the LED is attached to 36 | const int potPin = 27; // Potentiometer is connected to GPIO 27 (Analog ADC1_CH6) 37 | int32_t PWM_FEEDBACK_PIN = 18; 38 | 39 | //8-bit representation of a sinewave scaled to 0->255 40 | 41 | #define SIN_6_STEP_300Hz 0 42 | #define SIN_8_STEP_400Hz 0 43 | #define SIN_360_STEP_18KHz 1 44 | 45 | 46 | 47 | //6 step 48 | #if SIN_6_STEP_300Hz 49 | #define MODULATING_FREQ 300 // 300Hz 50 | #define MAX_STEPS 6 // discrete signal steps 51 | #define TRANSISTOR_SWITCH_STEP 2 // when to use other transistor bank 52 | int32_t sinetable[]= 53 | { 54 | 0, 55 | 255, 56 | 255, 57 | 0, 58 | 255, 59 | 255, 60 | }; 61 | #endif 62 | 63 | 64 | 65 | //8 step 66 | #if SIN_8_STEP_400Hz 67 | #define MODULATING_FREQ 400 // <-- 18KHz is 360 x 50 Hz for American 60Hz use number 21600 instead 68 | #define MAX_STEPS 8 // one pwm update per degree of mains sine 69 | #define TRANSISTOR_SWITCH_STEP 3 // when to use other transistor bank 70 | int32_t sinetable[]= 71 | { 72 | 0, 73 | 180, 74 | 255, 75 | 180, 76 | 0, 77 | 180, 78 | 255, 79 | 180 80 | }; 81 | #endif 82 | 83 | 84 | 85 | //360 step 86 | #if SIN_360_STEP_18KHz 87 | #define MODULATING_FREQ 18000 // <-- 18KHz is 360 x 50 Hz for American 60Hz use number 21600 instead 88 | #define MAX_STEPS 360 // one pwm update per degree of mains sine 89 | #define TRANSISTOR_SWITCH_STEP 179 // when to use other transistor bank 90 | int32_t sinetable[]= 91 | { 92 | 0 , 93 | 4 , 94 | 8 , 95 | 13 , 96 | 17 , 97 | 22 , 98 | 26 , 99 | 31 , 100 | 35 , 101 | 39 , 102 | 44 , 103 | 48 , 104 | 53 , 105 | 57 , 106 | 61 , 107 | 65 , 108 | 70 , 109 | 74 , 110 | 78 , 111 | 83 , 112 | 87 , 113 | 91 , 114 | 95 , 115 | 99 , 116 | 103 , 117 | 107 , 118 | 111 , 119 | 115 , 120 | 119 , 121 | 123 , 122 | 127 , 123 | 131 , 124 | 135 , 125 | 138 , 126 | 142 , 127 | 146 , 128 | 149 , 129 | 153 , 130 | 156 , 131 | 160 , 132 | 163 , 133 | 167 , 134 | 170 , 135 | 173 , 136 | 177 , 137 | 180 , 138 | 183 , 139 | 186 , 140 | 189 , 141 | 192 , 142 | 195 , 143 | 198 , 144 | 200 , 145 | 203 , 146 | 206 , 147 | 208 , 148 | 211 , 149 | 213 , 150 | 216 , 151 | 218 , 152 | 220 , 153 | 223 , 154 | 225 , 155 | 227 , 156 | 229 , 157 | 231 , 158 | 232 , 159 | 234 , 160 | 236 , 161 | 238 , 162 | 239 , 163 | 241 , 164 | 242 , 165 | 243 , 166 | 245 , 167 | 246 , 168 | 247 , 169 | 248 , 170 | 249 , 171 | 250 , 172 | 251 , 173 | 251 , 174 | 252 , 175 | 253 , 176 | 253 , 177 | 254 , 178 | 254 , 179 | 254 , 180 | 254 , 181 | 254 , 182 | 255 , 183 | 254 , 184 | 254 , 185 | 254 , 186 | 254 , 187 | 254 , 188 | 253 , 189 | 253 , 190 | 252 , 191 | 251 , 192 | 251 , 193 | 250 , 194 | 249 , 195 | 248 , 196 | 247 , 197 | 246 , 198 | 245 , 199 | 243 , 200 | 242 , 201 | 241 , 202 | 239 , 203 | 238 , 204 | 236 , 205 | 234 , 206 | 232 , 207 | 231 , 208 | 229 , 209 | 227 , 210 | 225 , 211 | 223 , 212 | 220 , 213 | 218 , 214 | 216 , 215 | 213 , 216 | 211 , 217 | 208 , 218 | 206 , 219 | 203 , 220 | 200 , 221 | 198 , 222 | 195 , 223 | 192 , 224 | 189 , 225 | 186 , 226 | 183 , 227 | 180 , 228 | 177 , 229 | 173 , 230 | 170 , 231 | 167 , 232 | 163 , 233 | 160 , 234 | 156 , 235 | 153 , 236 | 149 , 237 | 146 , 238 | 142 , 239 | 138 , 240 | 135 , 241 | 131 , 242 | 127 , 243 | 123 , 244 | 119 , 245 | 115 , 246 | 111 , 247 | 107 , 248 | 103 , 249 | 99 , 250 | 95 , 251 | 91 , 252 | 87 , 253 | 83 , 254 | 78 , 255 | 74 , 256 | 70 , 257 | 65 , 258 | 61 , 259 | 57 , 260 | 53 , 261 | 48 , 262 | 44 , 263 | 39 , 264 | 35 , 265 | 31 , 266 | 26 , 267 | 22 , 268 | 17 , 269 | 13 , 270 | 8 , 271 | 4 , 272 | 0 , 273 | 5 , 274 | 9 , 275 | 14 , 276 | 18 , 277 | 23 , 278 | 27 , 279 | 32 , 280 | 36 , 281 | 40 , 282 | 45 , 283 | 49 , 284 | 54 , 285 | 58 , 286 | 62 , 287 | 66 , 288 | 71 , 289 | 75 , 290 | 79 , 291 | 84 , 292 | 88 , 293 | 92 , 294 | 96 , 295 | 100 , 296 | 104 , 297 | 108 , 298 | 112 , 299 | 116 , 300 | 120 , 301 | 124 , 302 | 128 , 303 | 132 , 304 | 136 , 305 | 139 , 306 | 143 , 307 | 147 , 308 | 150 , 309 | 154 , 310 | 157 , 311 | 161 , 312 | 164 , 313 | 168 , 314 | 171 , 315 | 174 , 316 | 178 , 317 | 181 , 318 | 184 , 319 | 187 , 320 | 190 , 321 | 193 , 322 | 196 , 323 | 199 , 324 | 201 , 325 | 204 , 326 | 207 , 327 | 209 , 328 | 212 , 329 | 214 , 330 | 217 , 331 | 219 , 332 | 221 , 333 | 224 , 334 | 226 , 335 | 228 , 336 | 230 , 337 | 232 , 338 | 233 , 339 | 235 , 340 | 237 , 341 | 239 , 342 | 240 , 343 | 242 , 344 | 243 , 345 | 244 , 346 | 246 , 347 | 247 , 348 | 248 , 349 | 249 , 350 | 250 , 351 | 251 , 352 | 252 , 353 | 252 , 354 | 253 , 355 | 254 , 356 | 254 , 357 | 255 , 358 | 255 , 359 | 255 , 360 | 255 , 361 | 255 , 362 | 255 , 363 | 255 , 364 | 255 , 365 | 255 , 366 | 255 , 367 | 255 , 368 | 254 , 369 | 254 , 370 | 253 , 371 | 252 , 372 | 252 , 373 | 251 , 374 | 250 , 375 | 249 , 376 | 248 , 377 | 247 , 378 | 246 , 379 | 244 , 380 | 243 , 381 | 242 , 382 | 240 , 383 | 239 , 384 | 237 , 385 | 235 , 386 | 233 , 387 | 232 , 388 | 230 , 389 | 228 , 390 | 226 , 391 | 224 , 392 | 221 , 393 | 219 , 394 | 217 , 395 | 214 , 396 | 212 , 397 | 209 , 398 | 207 , 399 | 204 , 400 | 201 , 401 | 199 , 402 | 196 , 403 | 193 , 404 | 190 , 405 | 187 , 406 | 184 , 407 | 181 , 408 | 178 , 409 | 174 , 410 | 171 , 411 | 168 , 412 | 164 , 413 | 161 , 414 | 157 , 415 | 154 , 416 | 150 , 417 | 147 , 418 | 143 , 419 | 139 , 420 | 136 , 421 | 132 , 422 | 128 , 423 | 124 , 424 | 120 , 425 | 116 , 426 | 112 , 427 | 108 , 428 | 104 , 429 | 100 , 430 | 96 , 431 | 92 , 432 | 88 , 433 | 84 , 434 | 79 , 435 | 75 , 436 | 71 , 437 | 66 , 438 | 62 , 439 | 58 , 440 | 54 , 441 | 49 , 442 | 45 , 443 | 40 , 444 | 36 , 445 | 32 , 446 | 27 , 447 | 23 , 448 | 18 , 449 | 14 , 450 | 9 , 451 | 5 , 452 | 1 , 453 | }; 454 | #endif 455 | 456 | 457 | 458 | 459 | uint8_t readPot =0; 460 | int32_t ctr=0; 461 | int32_t ctr2=180; 462 | int32_t val2 = 0; 463 | /** 464 | * @brief IRAM_ATTR 465 | * An interrupt service routine that runs whenever a rising edge is detected on 466 | * the feedback I/O pin. We generate a PWM pulse at 18KHz for 50Hz, or 21.6Khz 467 | * for a 60Hz pulse. the PWM output pin is fed back into the chip as an 468 | * interrupt and we retrigger from it. 469 | * Every time an interrupt is received we load in the next PWM value from the 470 | * sine table. this allows each degree of the 50 or 60Hz waveform to have a 471 | * separate pulse associated with it 472 | */ 473 | void IRAM_ATTR isr() 474 | { 475 | 476 | /// 477 | /// 1. Compute next PWM register value 478 | /// 479 | int32_t val = sinetable[ctr] ; //scale the sine tablkee 480 | 481 | 482 | /// 483 | /// 2. select which transitor bank to use 484 | /// 485 | if(ctr>TRANSISTOR_SWITCH_STEP) 486 | { 487 | ledcWrite(DRIVE1_PWM, val); //signal to mosfet gate 488 | ledcWrite(DRIVE2_PWM, 0); //signal to mosfet gate 489 | } 490 | else 491 | { 492 | ledcWrite(DRIVE1_PWM, 0); //signal to mosfet gate 493 | ledcWrite(DRIVE2_PWM, val); //signal to mosfet gate 494 | } 495 | 496 | 497 | /// 498 | /// 3. increment counter 499 | /// 500 | ctr += 1; 501 | if(ctr==MAX_STEPS) 502 | { 503 | ctr = 0; 504 | readPot = 1; //allow amplitude to change 505 | } 506 | 507 | 508 | /// 509 | /// 4. self triggering interrupt (mandatory, it just has to have some non-zero value) 510 | /// 511 | ledcWrite(SELF_TRIGGERING_IRQ, 1); // set the brightness of the LED 512 | 513 | } 514 | 515 | 516 | 517 | 518 | 519 | 520 | int32_t t = 0; 521 | int32_t oldPotValue = -1; 522 | 523 | const int numReadings = 10; 524 | 525 | int readings[numReadings]; // the readings from the analog input 526 | int readIndex = 0; // the index of the current reading 527 | int total = 0; // the running total 528 | int average = 0; // the average 529 | int pV = 0; 530 | 531 | 532 | /** 533 | * @brief setup 534 | * the setup routine runs once when you press reset: 535 | */ 536 | void setup() { 537 | 538 | 539 | // Initialize channels 540 | // channels 0-15, resolution 1-16 bits, freq limits depend on resolution 541 | // ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits); 542 | ledcSetup(SELF_TRIGGERING_IRQ, MODULATING_FREQ, 8); // 18 kHz PWM, 8-bit resolution 543 | ledcSetup(DRIVE1_PWM, MODULATING_FREQ, 8); // 18 kHz PWM, 8-bit resolution 544 | ledcSetup(DRIVE2_PWM, MODULATING_FREQ, 8); // 18 kHz PWM, 8-bit resolution 545 | 546 | 547 | ledcAttachPin(led_gpio, SELF_TRIGGERING_IRQ); // assign a led pins to a channel 548 | ledcAttachPin(PUSHPULL_DRIVE_1, DRIVE1_PWM); 549 | ledcAttachPin(PUSHPULL_DRIVE_2, DRIVE2_PWM); 550 | 551 | 552 | pinMode(PWM_FEEDBACK_PIN, INPUT_PULLDOWN); 553 | attachInterrupt(PWM_FEEDBACK_PIN, isr, RISING); //this is wired to the PWM output 554 | 555 | ledcWrite(SELF_TRIGGERING_IRQ, pwm); //signal to mosfet gate 556 | ledcWrite(DRIVE1_PWM, 127); //signal to mosfet gate 557 | ledcWrite(DRIVE2_PWM, 127); //signal to mosfet gate 558 | Serial.begin(115200); 559 | 560 | for (int thisReading = 0; thisReading < numReadings; thisReading++) { 561 | readings[thisReading] = 0; 562 | } 563 | } 564 | 565 | 566 | 567 | const int CAP_ADC = 255;///176; 568 | 569 | /** 570 | * @brief loop 571 | * Shall read ADC value and allow user to modify PWM amplitude by means of 572 | * potentiometer. We scale the SPWM in fact, allowing a synthesised lower voltage 573 | */ 574 | void loop() { 575 | 576 | if(readPot) 577 | { 578 | 579 | t+=1; 580 | if(20==t) 581 | { 582 | readPot = 0; 583 | int p = analogRead(potPin); //https://randomnerdtutorials.com/esp32-adc-analog-read-arduino-ide/ 584 | pV = p/16; 585 | // subtract the last reading: 586 | total = total - readings[readIndex]; 587 | // read from the sensor: 588 | readings[readIndex] = pV; 589 | // add the reading to the total: 590 | total = total + readings[readIndex]; 591 | // advance to the next position in the array: 592 | readIndex = readIndex + 1; 593 | 594 | // if we're at the end of the array... 595 | if (readIndex >= numReadings) { 596 | // ...wrap around to the beginning: 597 | readIndex = 0; 598 | } 599 | 600 | // calculate the average: 601 | average = total / numReadings; 602 | 603 | 604 | potValue = average; 605 | potValue = 255; 606 | if(potValue>CAP_ADC) 607 | { 608 | potValue=CAP_ADC; 609 | } 610 | if(oldPotValue!=potValue) 611 | { 612 | Serial.printf("ADCM %u\n", potValue); 613 | oldPotValue=potValue; 614 | } 615 | t=0; 616 | 617 | if(potValue<1) 618 | { 619 | potValue = 0; 620 | } 621 | if(potValue>255) 622 | { 623 | potValue = 255; 624 | } 625 | } 626 | 627 | } 628 | } 629 | -------------------------------------------------------------------------------- /sin.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cybernetic-research/esp32-sine-inverter/bd0fc3d794e84ee7f7c5a4d8cbdbd5ec3421d87d/sin.xlsx --------------------------------------------------------------------------------