Advertisement
TheArcticGentoo

Master-Slave Serial Grapher

Sep 25th, 2018
134
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 14.23 KB | None | 0 0
  1. //Copyright (C) Andrew Spangler 2018
  2. //Feel free to use this, just credit me.
  3. #define VERSION "0.8.9"                //version 8 was working independent graphs, version .9 is serial input enabled, 1 is graph number, size, location instructed over serial, 2 is library
  4. #include <Adafruit_GFX.h>
  5. #include <Adafruit_SSD1306.h>
  6. #include <Wire.h>
  7.  
  8. /*-----Role Selector-----*/
  9. #define ROLE_SEND     0
  10. #define ROLE_RECEIVE  1
  11. #define ROLE_PIN      4
  12. bool ROLE;
  13.  
  14. /*-----Serial Stuff-----*/
  15. String inputString = "";            // a String to hold incoming data
  16. bool stringComplete = false;        // whether the string is complete
  17. #define enableDebugSpew false        // Set to true to enable debug spew into the serial console
  18. String screenString = "";
  19.  
  20. //Serial Mode Switching
  21. #define MODE_GRAPH 0
  22. #define PORTABLE_DEBUG 1
  23.  
  24. #define enableSerialSetup 1
  25. int mode[2] = {0, enableSerialSetup};     //second value tracks if setup has run.
  26.  
  27.  
  28.  
  29. /*-----OLED Stuff-----*/
  30. #define OLED_RESET LED_BUILTIN            //My LCD doesn't support a reset pin (wish it did for esp8266, this code needs a full power cycle to work properly otherwise the screen gets stuck on reset)
  31. Adafruit_SSD1306 display(OLED_RESET);     //this makes the led flash when the display would normally reset, most I2C SSD1306's don't have a reset pin but it must be defined to set up the display.
  32.  
  33. #define TEXT_SMALL 1                      //Personal Ease-of-reading definitions
  34. #define TEXT_BIG   2
  35. #define TEXT_HUGE  3
  36.  
  37.  
  38. /*-----Graph Stuff-----*/
  39. #define graphBufferSize 33                //Set buffer size, SET TO (WIDTH OF YOUR WIDEST GRAPH + 1) OR YOU WILL EXCEED THE ARRAY.
  40. #define overflowProtect true              //If set to true will prevent graph function from running if the passed graph width is grater than the buffer
  41.  
  42.  
  43.  
  44. int graphMax = 1023;                      //max value to pass to the graph, exceeding this value will display at the top of the graph
  45.  
  46.  
  47. struct graphStruct {
  48.   uint8_t gx;
  49.   uint8_t gy;
  50.   uint8_t gw;
  51.   uint8_t gh;
  52.   int gVal;
  53.   int graphBuffer[graphBufferSize];
  54.   bool isReady;
  55. };
  56.  
  57. struct setupPacketStruct {                   //NO STRINGS IN HERE. GARBAGE OTHERWISE;
  58.       uint8_t gx;
  59.       uint8_t gy;
  60.       uint8_t gw;                               //Graph buffer size is |TO BE| determined by receiver as gw+2 |NYI|
  61.       uint8_t gh;
  62.       uint8_t gn;
  63. };
  64.  
  65. #define graphNum 4                    //Set this to the number of Graphs you want.
  66. graphStruct graph[graphNum] = {       //Each set of brackets is an instance of a graph, one for each specified in graphNum
  67.   //Graph 1                           //Usage: {LeftX, TopY, width, height}
  68.   {0, 0, 31, 32},
  69.   //Graph 2
  70.   {32, 0, 31, 32},
  71.   //Graph 3
  72.   {64, 0, 31, 32},
  73.   //Graph 4
  74.   {96, 0, 31, 32},
  75. };
  76.  
  77.  
  78.  
  79. /*-------FPS/LPS Counter-------*/
  80. unsigned long timer;          /////DO NOT CALL VARIABLE "time" on ESP8266, CAUSES PROBLEMS W/ COMPILER, works on the trinket 3v. nano also.
  81. int loops[2];  //counts loops, 0 for current number of loops this second, 1 to store the last value to reprint on the I2C LCD
  82. unsigned long timerA, timerB, timerC, timerD;
  83.  
  84.  
  85. void setup() {
  86.   Serial.begin(9600);                        // Start Serial
  87.   delay(40);
  88.   //Determine Role:
  89.   pinMode(ROLE_PIN, INPUT_PULLUP);
  90.   ROLE = digitalRead(ROLE_PIN);
  91.   display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x32)
  92.   display.display();
  93.   delay(1000);
  94.   display.clearDisplay();                     //To reduce program size you can edit the header to remove the adafruit logo but then you may not redistribute it
  95.   displayCredits();
  96.   display.clearDisplay();
  97. }
  98.  
  99.  
  100. void loop() {
  101.   if (mode[1]) {
  102.     graphSetup();
  103.   }
  104.   if (mode[0] == MODE_GRAPH) {
  105.     graphLoop();
  106.   }
  107.   fps(0, 0);
  108.   display.display();
  109.  
  110. }
  111.  
  112.  
  113.  
  114.  
  115. void graphLoop() {
  116.  
  117. //-----------------------------------
  118. //Multi Rate Random Data Maker TEST//
  119.   if (timerA + 1000 <= (millis() )) {
  120.     graphAdd(0, random(1023));
  121.     timerA = millis();
  122.   }
  123.   if (timerB + 35 <= (millis() )) {
  124.     graphAdd(1, random(1023));
  125.     timerB = millis();
  126.   }
  127.   if (timerC + 350 <= (millis() )) {
  128.     graphAdd(2, random(1023));
  129.     timerC = millis();
  130.   }
  131.   if (timerD + 800 <= (millis() )) {
  132.     graphAdd(3, random(1023));
  133.     timerD = millis();
  134.   }
  135. //-----------------------------------
  136.  
  137.   for (int i = 0; i < graphNum; i++) {
  138.     if (graph[i].isReady == true) {
  139.       graph[i].isReady = false;
  140.       grapher(graph[i].gx, graph[i].gy, graph[i].gw, graph[i].gh, graph[i].gVal, i);
  141.     }
  142.   }
  143. }
  144.  
  145. void graphAdd(int graphNumber, int newVal) {
  146.   graph[graphNumber].gVal = newVal;
  147.   graph[graphNumber].isReady = true;
  148. }
  149.  
  150. void grapher(int gx, int gy, int gw, int gh, int inVal, int bufferNumber) {               //Pass the leftmost pixel (gx), topmost pixel (gy), graph width (gw), graph height (gh), and new value to add (inVal)
  151.   if (overflowProtect) {
  152.     display.drawRect(gx, gy, gw, gh, WHITE);                             //Draw a rectangle aroung the graph
  153.     display.fillRect(gx + 1, gy + 1, gw - 2, gh - 2, BLACK);
  154.     if (gw + 1 < graphBufferSize) {
  155.       graph[bufferNumber].graphBuffer[0] = ((inVal * gh) / graphMax);                           //Set the first value of the graphBuffer array to the input value scaled to the graph height and max input value
  156.       for (int i = gw - 2; i >= 0; i--) {                                   //Draw the graph, shifting the array down one notch so the last value is lost and the first value is duplicated between the first and second location in the array, making room for new data on next call and shifting it down
  157.         display.drawLine((gx + i + 1), (gy + (gh) - graph[bufferNumber].graphBuffer[i + 1] + 1), (gx + i), (gy + (gh) - graph[bufferNumber].graphBuffer[i] + 1), WHITE);
  158.         graph[bufferNumber].graphBuffer[i + 1] = graph[bufferNumber].graphBuffer[i];
  159.       }
  160.     } else {
  161.       display.setTextSize(1);
  162.       display.setCursor(gx + 2, gy + 1);
  163.       display.println("Buff");
  164.       display.setCursor(gx + 2, gy + 9);
  165.       display.println("Too");
  166.       display.setCursor(gx + 2, gy + 17);
  167.       display.println("Small");
  168.     }
  169.   } else {
  170.     /*
  171.      * once I have the whole grapher fleshed out I'll add in the non-overflow proteced code with #defined if statements to
  172.      * prevent the compiler from adding anything unneeded
  173.       graph[bufferNumber].graphBuffer[0] = ((inVal * gh) / graphMax);
  174.       display.drawRect(gx, gy, gw, gh, WHITE);
  175.       for (int i = gw - 2; i >= 0; i--) {
  176.       display.drawLine((gx + i + 1), (gy + (gh) - graph[bufferNumber].graphBuffer[i + 1] + 1), (gx + i), (gy + (gh) - graph[bufferNumber].graphBuffer[i] + 1), WHITE);
  177.       graph[bufferNumber].graphBuffer[i + 1] = graph[bufferNumber].graphBuffer[i];
  178.  
  179.       }
  180.     */
  181.   }
  182. }
  183.  
  184.  
  185.  
  186. //FPS Counter, basically updates once a second and displays how many times the program has looped since then
  187. void fps(int xpos, int ypos) {
  188.   if (timer + 1000 <= (millis() )) {
  189.     loops[1] = loops[0];
  190.     timer = millis();
  191.     loops[0] = 0;
  192.   } else {
  193.     loops[0]++;
  194.   }
  195.   display.setTextSize(1);                            //set text size
  196.   display.fillRect(xpos, ypos, xpos + 12, ypos + 7, WHITE);
  197.   display.setTextColor(BLACK);
  198.   display.setCursor(xpos, ypos);                //sets text line, left aligned,
  199.   display.println(loops[1]);
  200. }
  201.  
  202.  
  203. void displayCredits() {
  204.   display.setCursor(0, 0);
  205.   display.setTextSize(1);
  206.   display.setTextColor(WHITE);
  207.   display.println("Lyfe's Serial Grapher");
  208.   display.setCursor(0, 9);
  209.   switch (ROLE) {
  210.     case ROLE_SEND:
  211.       display.println("ROLE: TX");
  212.       break;
  213.     case ROLE_RECEIVE:
  214.       display.println("ROLE: RX");
  215.       break;
  216.     default:
  217.       display.println("ROLE: ERROR");
  218.       display.display();
  219.       while (1);
  220.       break;
  221.   }
  222.  
  223.   display.setCursor(0, 17);
  224.   String versionString = "Version: " + String(VERSION);
  225.   display.println(versionString);
  226.   display.display();
  227.   delay(1000);
  228. }
  229.  
  230. /*---Display Print Functions, structured for different data types, also prints t/f for booleans---*/
  231. //Pass String
  232. void displayPrint(String instring, int line, int textSize, bool clear) {
  233.   if (clear) {
  234.     display.clearDisplay();  //only clear if specified
  235.   }
  236.   display.setTextSize(textSize);                            //set text size
  237.   display.setTextColor(WHITE);                              //one-color displays are always "white" even if the oled is a different color
  238.   display.setCursor(0, line * 8 * textSize);                //sets text line, left aligned,
  239.   display.println(instring);                                //print data
  240. }
  241.  
  242. //Pass Int
  243. void displayPrint(int instring, int line, int textSize, bool clear) {
  244.   if (clear) {
  245.     display.clearDisplay();
  246.   }
  247.   display.setTextSize(textSize);
  248.   display.setTextColor(WHITE);
  249.   display.setCursor(0, line * 8 * textSize);
  250.   display.println(instring);
  251. }
  252.  
  253. //Pass Bool
  254. void displayPrint(bool instring, int line, int textSize, bool clear) {
  255.   if (clear) {
  256.     display.clearDisplay();
  257.   }
  258.   display.setTextSize(textSize);
  259.   display.setTextColor(WHITE);
  260.   display.setCursor(0, line * 8 * textSize);
  261.   if ( instring ) {
  262.     display.println("True");
  263.   } else {
  264.     display.println("False");
  265.   }
  266. }
  267.  
  268. //Pass Float
  269. void displayPrint(float instring, int line, int textSize, bool clear) {
  270.   if (clear) {
  271.     display.clearDisplay();
  272.   }
  273.   display.setTextSize(textSize);
  274.   display.setTextColor(WHITE);
  275.   display.setCursor(0, line * 8 * textSize);   //character line is 7 pixels high
  276.   display.println(instring);
  277. }
  278.  
  279.  
  280. void serialEvent() {
  281.   while (Serial.available()) {
  282.     // get the new byte:
  283.     char inChar = (char)Serial.read();
  284.     if (inChar == '\n') {
  285.       stringComplete = true;
  286.       if (enableDebugSpew) {
  287.         Serial.println("\n\n_______________________________");
  288.         Serial.print("Received via Serial: ");
  289.         Serial.println(inputString);
  290.       }
  291.     } else {
  292.       inputString += inChar;
  293.     }
  294.   }
  295. }
  296.  
  297.  
  298.  
  299.  
  300.  
  301.  
  302.  
  303. //CODE IN PROGRESS
  304.  
  305. #define SEND_REQUEST  0x60
  306. #define ACK           0x61
  307.  
  308.  
  309. #define MAX_RETRIES   10
  310.  
  311.  
  312. #define ACK_ERR     0x40
  313. #define TIMEOUT_ERR 0x41
  314.  
  315. void graphSetup() {
  316.  
  317.   if (ROLE == ROLE_RECEIVE) {
  318.     mode[1] = false;
  319.     displayPrint(String("Waiting"), 1, 2, true);
  320.     display.display();
  321.     int waitingDots = 0;
  322.     unsigned long dotTimer = millis();
  323.     String waitString = "Waiting";
  324.     while (!stringComplete) {
  325.       if (dotTimer + 500 <= (millis() )) {
  326.         String waitString = "Waiting";
  327.         for (int i = waitingDots; i > 0; i--) {
  328.           waitString += ".";
  329.         }
  330.  
  331.         if (waitingDots == 3) {
  332.           waitingDots = 0;
  333.         }
  334.         else {
  335.           waitingDots++;
  336.         }
  337.         displayPrint(waitString, 1, 2, true);
  338.         display.display();
  339.         dotTimer = millis();
  340.  
  341.       }
  342.       serialEvent();
  343.       mode[1] = !mode[1];
  344.     }
  345.  
  346.     switch ((inputString.toInt())) {
  347.       case SEND_REQUEST:                                                    //Request to set up graph;
  348.         mode[0] = MODE_GRAPH;
  349.         displayPrint(String("Ready"), 0, 2, true);
  350.         displayPrint(inputString, 1, 2, false);
  351.         display.display();
  352.         graphSetupReceive();
  353.  
  354.  
  355.  
  356.        
  357.        
  358.         while(true);
  359.         break;
  360.  
  361.       default:
  362.         displayPrint(String("ERROR"), 0, 2, true);                          
  363.         display.setTextColor(WHITE);
  364.         display.setTextSize(TEXT_SMALL);
  365.         /*  Wrote this because I though the displayPrint function wasn't printing the error code properly, It was.
  366.         for ( int i = 0; i < sizeof(inputString); i++){
  367.           display.setCursor(7 * i, 21);              
  368.           display.println(inputString.charAt(i));                
  369.         }
  370.         */
  371.         displayPrint(String(inputString), 3, 1, false);
  372.        
  373.         display.display();
  374.         while (true);
  375.         break;
  376.  
  377.  
  378.  
  379.  
  380.  
  381.     }
  382.   }
  383.  
  384.   if (ROLE == ROLE_SEND) {
  385.     stringComplete = false;
  386.     displayPrint(String("SENDING"), 0, 2, true);
  387.     display.display();
  388.     String packetString;                    //Used to send the bytes instructing the receiver to go into graph mode and prepare to receive graph setup instructions |NYI|
  389.     Serial.println(SEND_REQUEST);
  390.     displayPrint(SEND_REQUEST, 0, 2, true);
  391.     display.display();
  392.     graphSetupSend();
  393.   }
  394. }
  395.  
  396. bool errorHandler(int errByte){
  397.   display.clearDisplay();
  398.   switch(errByte){
  399.     case ACK_ERR:
  400.       displayPrint(String("ACK_ERR"), 0, 2, true);
  401.       display.display();
  402.       break;
  403.     case TIMEOUT_ERR:
  404.       displayPrint(String("TIMEOUT_ERR"), 0, 2, true);
  405.       display.display();
  406.       break;
  407.     default:
  408.       displayPrint(String("MISSINGNO."), 0, 2, true);
  409.       display.display();
  410.       break;
  411.     while(true);
  412.   }
  413. }
  414.  
  415.  
  416. void graphSetupReceive() {
  417.   serialWaitWithTimout();
  418.   setupPacketStruct packet[inputString.toInt()];                  //Receive the number of graphs and set up struct array
  419.   displayPrint(inputString, 0, 2, true);
  420.   serialWaitWithTimout();
  421.  
  422.   for (int i = 0; i < graphNum; i++ ){
  423.     delete graph[i];
  424.   }
  425. }
  426.  
  427.  
  428. void graphSetupSend() {      
  429.   //Starts after first ACK in receive
  430.   Serial.println(graphNum);                                       //Send number of graphs to initiate graphs
  431.   waitForACK();
  432.  
  433.  
  434.  
  435.   setupPacketStruct packet[graphNum];
  436.   for (int i = 0; i < graphNum; i++) {
  437.     packet[i].gx = graph[i].gx;
  438.     packet[i].gy = graph[i].gy;
  439.     packet[i].gw = graph[i].gw;
  440.     packet[i].gh = graph[i].gh;
  441.     packet[i].gn = i;
  442.     Serial.write((uint8_t *)&packet[i], sizeof(packet[i]));
  443.   }
  444.  
  445.  
  446. }
  447.  
  448.  
  449.  
  450. bool waitForACK(){                    //checks for ack and reports either timeout or ack error
  451.   inputString = "";
  452.   unsigned long timeOut = millis();
  453.   while (!stringComplete){
  454.     serialEvent();
  455.     if(timeOut + 1000 <= millis()){
  456.       errorHandler(TIMEOUT_ERR);
  457.     }
  458.   stringComplete = false;
  459.   }
  460.   int tempString = inputString.toInt();
  461.   inputString = "";
  462.   if (tempString == ACK){
  463.     return true;
  464.   } else {
  465.     errorHandler(ACK_ERR);
  466.   }
  467. }
  468.  
  469.  
  470. void serialWaitWithTimout() {
  471.   inputString = "";
  472.   unsigned long timeOut = millis();
  473.   while (!stringComplete){
  474.     serialEvent();
  475.     if(timeOut + 1000 <= millis()){
  476.       errorHandler(TIMEOUT_ERR);
  477.     }
  478.   stringComplete = false;
  479.   }
  480. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement