Intro
The kit arrived, so I can start to learn what Nicla Vision is, but this blog will focus on integrating a couple of subsystems to work out how they interface to each other.
I want the HuskyLens to put the correct name to classified objects. This requires some programming.
This episode also covers the firmware needed to query HuskyLens about what it has recognized.
I worked on getting a LoRa link working as well but it looks like one of the LoRa radios is not working, but at least there is a start on the firmware.
The new speech synthesis modules arrived so the cast now have their own voices.
I will use the Arduino MKR 1300 to control the HuskyLens and the new speech synthesis module. This will allow the system to interact with the cast of characters by recognizing each one and using that information to trigger interactive speech.
A stretch goal for this blog is to get wireless interaction started - working out LoRa communications.
The Build For This Episode:
These intermediate builds are needed because the PCBs have not arrived and I need some hardware to test firmware. I had to clean them up from the first breadboard lashup.
The Crystal System
The Crystal System is the base station back at the ranch. It has a MKR WAN 1300, an LCD and a single voice speech synthesis module.
The Lora System
The Lora System is the remote system out in the field - it recognizes bees and insects and reports back to the Crystal System.
It consists of a Huskylens, a MKR WAN 1310 and a multi-voice speech synthesis module.
Adding Real Names To Classified Objects
To add names to recognized opjects requires programming from an external MCU. For this I used an Arduino UNO because it is not clear what voltages Husky uses when communicating. It can run off 3.3 or 5V but when running off 5V, will it hurt connected devices that run off 3.3V? I didn't take any chances and used a 5V UNO. Note the custom 3D printed GoPro mount for the Huskylens. In this image the Huskylens has identified the ladybug and correctly labelled the image:
Here is the program used to assign names to the recognized objects:
/*************************************************** HUSKYLENS utility to name classified objects The program runs on an Arduino UNO Doug Wong 2023 **************************************************/ #include "HUSKYLENS.h" #include "SoftwareSerial.h" HUSKYLENS huskylens; SoftwareSerial mySerial(10, 11); // RX, TX //HUSKYLENS green line >> Pin 10; blue line >> Pin 11 void setNewName (String newname, uint8_t ID){ while (!huskylens.setCustomName(newname, ID)) { Serial.println("Custom name failed"); } } void setup() { Serial.begin(9600); mySerial.begin(9600); while (!huskylens.begin(mySerial)) { Serial.println("Begin failed!"); Serial.println("1.Please recheck the \"Protocol Type\" in HUSKYLENS (General Settings>>Protocol Type>>Serial 9600)"); Serial.println("2.Please recheck the connection."); delay(100); } delay(100); // change names setNewName ("BeeOne", 1); setNewName ("BeeTwo", 2); setNewName ("Spiderman", 3); setNewName ("Cindy Cicada", 4); setNewName ("Grasshopper", 5); setNewName ("Ladybug", 6); } void loop() { }
Querying Husky to Know What Obejct Was Recognized
Here is the firmware used to sort out how to query the Huskylens and find out what it recognized:
/*************************************************** HUSKYLENS Oject Classification Monitor Program to test how to query the Huskylens to get IDs of recognized objects Doug Wong 2023 ****************************************************/ #include "HUSKYLENS.h" #include "SoftwareSerial.h" HUSKYLENS huskylens; //HUSKYLENS green line >> SDA; blue line >> SCL int ID0 = 0; //not learned results. Grey result on HUSKYLENS screen int ID1 = 1; //first learned result - BeeOne int ID2 = 2; //second learned result - TwoBee int ID3 = 3; //third learned result - Spiderman int ID4 = 4; //fourth learned result - Cindy int ID5 = 5; //fifth learned result - Grasshopper int ID6 = 6; //sixth learned result - Ladybug SoftwareSerial mySerial(10, 11); // RX, TX void printResult(HUSKYLENSResult result); void setup() { Serial.begin(9600); mySerial.begin(9600); while (!huskylens.begin(mySerial)) { Serial.println(F("Begin failed!")); Serial.println(F("1.Please recheck the \"Protocol Type\" in HUSKYLENS (General Settings>>Protocol Type>>I2C)")); Serial.println(F("2.Please recheck the connection.")); delay(100); } huskylens.writeAlgorithm(ALGORITHM_OBJECT_RECOGNITION); } void loop() { if (huskylens.request()) //request all blocks and arrows from HUSKYLENS if (huskylens.requestBlocks(ID1)) //request blocks tangged ID == ID1 from HUSKYLENS { for (int i = 0; i < huskylens.count(ID1); i++) { HUSKYLENSResult result = huskylens.get(ID1, i); printResult(result); } } if (huskylens.requestBlocks(ID2)) //request blocks tangged ID == ID2 from HUSKYLENS { for (int i = 0; i < huskylens.count(ID2); i++) { HUSKYLENSResult result = huskylens.get(ID2, i); printResult(result); } } if (huskylens.requestBlocks(ID3)) //request blocks tangged ID == ID3 from HUSKYLENS { for (int i = 0; i < huskylens.count(ID3); i++) { HUSKYLENSResult result = huskylens.get(ID3, i); printResult(result); } } if (huskylens.requestBlocks(ID4)) //request blocks tangged ID == ID4 from HUSKYLENS { for (int i = 0; i < huskylens.count(ID4); i++) { HUSKYLENSResult result = huskylens.get(ID4, i); printResult(result); } } if (huskylens.requestBlocks(ID5)) //request blocks tangged ID == ID5 from HUSKYLENS { for (int i = 0; i < huskylens.count(ID5); i++) { HUSKYLENSResult result = huskylens.get(ID5, i); printResult(result); } } if (huskylens.requestBlocks(ID6)) //request blocks tangged ID == ID6 from HUSKYLENS { for (int i = 0; i < huskylens.count(ID6); i++) { HUSKYLENSResult result = huskylens.get(ID6, i); printResult(result); } } else { Serial.println("Fail to request objects from Huskylens!"); } } void printResult(HUSKYLENSResult result){ if (result.command == COMMAND_RETURN_BLOCK){//result is a block Serial.println(String()+F(",ID=")+result.ID); } else{//result is unknown. Serial.println("Object unknown!"); } delay (200); }
To demonstrate the recognition / naming ability of Husky a bracket was designed and printed to mount the Huskylens in front of a video camera. Here is what the HuskyLens bracket looks like - that keeps the HuskyLens correctly positioned in front of the video camera:
Here is a short video showing what that camera sees when looking at the Huskylens and showing what some of the voices sound like:
Here is the firmware running all the different voices:
/* Speech Syth Controller + MKR 1310 Uses a XFS5152 text-to-speech module to talk by Doug Wong 2023 */ #include <SPI.h> #include <LoRa.h> #define PIN_SCE 6 //chip select pin for LCD #define PIN_RESET 10 //reset pin for LCD #define PIN_DC 7 //data / command select pin for LCD #define PIN_SDIN 8 //serial data input pin for LCD (MOSI) #define PIN_SCLK 9 //SPI clock pin for LCD #define LCD_C LOW //this value select command mode on LCD DC #define LCD_D HIGH //this value select data mode on LCD DC #define LCD_X 84 //number of pixels on an LCD line #define LCD_Y 48 //number of vertical pixels on LCD const unsigned long SEND_INT = 5000; // Sends a msg on this interval (milliSec) char rsi; // receive signal strength int rssi; // recieve signal strength int packetSize; // LoRa recieved packet size char LoRaIn; // character received via LoRa char LoRaInStr[2]; char LoRaChar; String rsis; char rsia[4]; char LoRaString[1]; byte synback; bool news; //new sighting flag byte critr; //critter number char crits[2]; //critter number as a charchar LoRaChar; String critss; void speak(char* msg) { Serial1.write(0xFD); //Start Byte Serial1.write((byte)0x0); //length of message string - high byte Serial1.write(2 + strlen(msg)); //length of message - low byte Serial1.write(0x01); //command 01 = speak message Serial1.write((byte)0x0); //text encoding format 0-3 Serial1.write(msg); //message to speak } void waitForSpeech(unsigned long timeout = 60000) { unsigned long start = millis(); bool done = false; while ( ! done && (millis() - start) < timeout ) { while ( Serial1.available() ) { if ( Serial1.read() == 0x4F ) { done = true; break; } } } } // Speech Parameters // f0 - stress each syllable n0 - auto digital // f1 - normal synthesis n1 - digital number // g0 - auto determine language n2 - digital value // g1 - numeral, units, symbols o0 - "zero" // g2 - english synthesis o1 - European // h0 - auto word diction pn - n = pause duration in ms // h1 - letter by letter diction r0 - auto surname // h2 - word by word diction r1 - surname rules // i0 - no Pinyin sn - n = speech speed 0-10 // i1 - Pinyin tn - n = tone 0-10 // m3 - asian female voice vn - n = volume 0-10 // m51- male voice x0 - no tone // m52 - generic male x1 - use tone // m53 - generic female y0 - 1 = unitary // m54 - cartoon y1 - 1 = one // m55 - girl z0 - * # = symbols // =n - inflection n = 1-5 d - restore default //Chartacter Voices // Oh Bee One [h0][f1][m51][t3][s5] // Two Bee [f1][m53][t8][s6] // Lora [f1][m53][t5][s5] // Husky [f1][m51][t4][s5] // Spiderman [f1][m51][t6][s4] // Cindy Cicada [f1][m3][t7][s6] // Grasshopper [f1][m20][t2][s7] // Ladybug [f1][m55][t5][s6] // Crystal [f1][m53][t7][s5] // Ardu [f1][m51][t5][s5] // pixel map of character set for LCD static const byte ASCII[][5] = { {0x00, 0x00, 0x00, 0x00, 0x00} // 20 , {0x00, 0x00, 0x5f, 0x00, 0x00} // 21 ! , {0x00, 0x07, 0x00, 0x07, 0x00} // 22 " , {0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 # , {0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $ , {0x23, 0x13, 0x08, 0x64, 0x62} // 25 % , {0x36, 0x49, 0x55, 0x22, 0x50} // 26 & , {0x00, 0x05, 0x03, 0x00, 0x00} // 27 ' , {0x00, 0x1c, 0x22, 0x41, 0x00} // 28 ( , {0x00, 0x41, 0x22, 0x1c, 0x00} // 29 ) , {0x14, 0x08, 0x3e, 0x08, 0x14} // 2a * , {0x08, 0x08, 0x3e, 0x08, 0x08} // 2b + , {0x00, 0x50, 0x30, 0x00, 0x00} // 2c , , {0x08, 0x08, 0x08, 0x08, 0x08} // 2d - , {0x00, 0x60, 0x60, 0x00, 0x00} // 2e . , {0x20, 0x10, 0x08, 0x04, 0x02} // 2f / , {0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0 , {0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1 , {0x42, 0x61, 0x51, 0x49, 0x46} // 32 2 , {0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3 , {0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4 , {0x27, 0x45, 0x45, 0x45, 0x39} // 35 5 , {0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6 , {0x01, 0x71, 0x09, 0x05, 0x03} // 37 7 , {0x36, 0x49, 0x49, 0x49, 0x36} // 38 8 , {0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9 , {0x00, 0x36, 0x36, 0x00, 0x00} // 3a : , {0x00, 0x56, 0x36, 0x00, 0x00} // 3b ; , {0x08, 0x14, 0x22, 0x41, 0x00} // 3c < , {0x14, 0x14, 0x14, 0x14, 0x14} // 3d = , {0x00, 0x41, 0x22, 0x14, 0x08} // 3e > , {0x02, 0x01, 0x51, 0x09, 0x06} // 3f ? , {0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @ , {0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A , {0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B , {0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C , {0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D , {0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E , {0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F , {0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G , {0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H , {0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I , {0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J , {0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K , {0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L , {0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M , {0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N , {0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O , {0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P , {0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q , {0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R , {0x46, 0x49, 0x49, 0x49, 0x31} // 53 S , {0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T , {0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U , {0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V , {0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W , {0x63, 0x14, 0x08, 0x14, 0x63} // 58 X , {0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y , {0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z , {0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [ , {0x02, 0x04, 0x08, 0x10, 0x20} // 5c ¥ , {0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ] , {0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^ , {0x40, 0x40, 0x40, 0x40, 0x40} // 5f _ , {0x00, 0x01, 0x02, 0x04, 0x00} // 60 ` , {0x20, 0x54, 0x54, 0x54, 0x78} // 61 a , {0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b , {0x38, 0x44, 0x44, 0x44, 0x20} // 63 c , {0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d , {0x38, 0x54, 0x54, 0x54, 0x18} // 65 e , {0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f , {0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g , {0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h , {0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i , {0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j , {0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k , {0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l , {0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m , {0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n , {0x38, 0x44, 0x44, 0x44, 0x38} // 6f o , {0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p , {0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q , {0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r , {0x48, 0x54, 0x54, 0x54, 0x20} // 73 s , {0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t , {0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u , {0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v , {0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w , {0x44, 0x28, 0x10, 0x28, 0x44} // 78 x , {0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y , {0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z , {0x00, 0x08, 0x36, 0x41, 0x00} // 7b { , {0x00, 0x00, 0x7f, 0x00, 0x00} // 7c | , {0x00, 0x41, 0x36, 0x08, 0x00} // 7d } , {0x10, 0x08, 0x08, 0x10, 0x08} // 7e ← , {0x78, 0x46, 0x41, 0x46, 0x78} // 7f → }; void LcdCharacter(char character) // display a character on the LCD { LcdWrite(LCD_D, 0x00); // display a blank space before character for (int index = 0; index < 5; index++) // font uses 5 x 7 pixels { LcdWrite(LCD_D, ASCII[character - 0x20][index]); // display next column of pixels for this character } LcdWrite(LCD_D, 0x00); // display a blank space after character } void LcdClear(void) // display a blank screen { LcdWrite(LCD_C, 0x40 ); // command to set Y cursor position to 0 LcdWrite(LCD_C, 0x80 ); // command to set X cursor position to 0 for (int index = 0; index < LCD_X * LCD_Y / 8; index++) { LcdWrite(LCD_D, 0x00); // display a blank column of 8 pixels } // the LCD automatically steps to the next position } void LcdInitialise(void) { pinMode(PIN_SCE, OUTPUT); // set the chip select pin to be an output // pinMode(PIN_RESET, OUTPUT); pinMode(PIN_DC, OUTPUT); // set the DC pin to be an output pinMode(PIN_SDIN, OUTPUT); // set the MOSI pin to be an output pinMode(PIN_SCLK, OUTPUT); // set the SPI clock pin to be an output // digitalWrite(PIN_RESET, LOW); // digitalWrite(PIN_RESET, HIGH); LcdWrite(LCD_C, 0x21 ); // put LCD in Extended Commands mode. LcdWrite(LCD_C, 0xA8 ); // Set LCD Vop (Contrast). 0x80 - 0xFF (0xA8 for 1300, 0xCC for 1310) LcdWrite(LCD_C, 0x05 ); // Set Temp coefficent. 0x04 - 0x07 LcdWrite(LCD_C, 0x16 ); // LCD bias mode 1:48. 0x10 - 0x17 LcdWrite(LCD_C, 0x20 ); // LCD Basic Commands LcdWrite(LCD_C, 0x0C ); // put LCD back in normal mode. } void LcdString(char *characters) // display a sting of characters on LCD { while (*characters) { LcdCharacter(*characters++); } } void LcdWrite(byte dc, byte data) // display a byte as 8 vertical pixels { digitalWrite(PIN_DC, dc); // control the DC pin - should be data digitalWrite(PIN_SCE, LOW); // apply chip select to LCD shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data); // send byte as serial bit stream digitalWrite(PIN_SCE, HIGH); // deselect LCD } void setup() { Serial1.begin(9600); // this serial port is used for controlling speech LcdInitialise(); // display a splash screen LcdClear(); LcdString(" Husky "); LcdString(" BUG Detect "); LcdString(" and "); LcdString(" SPEECH "); LcdString(" by "); LcdString(" DOUG WONG "); delay(1000); // show the splash screen for 1 second LcdClear(); // if (!LoRa.begin(915E6)) { //start LoRa radio // LcdString("LoRa fail"); // while (1); // } // LcdClear(); news = 1; critr = 4; //3=Lora, 1=B1, 2=2B, 4=Husky, 5=Spiderman, 6=Cindy, 7=Grashopper, 8=Ladybug, 9=background } void loop() // main loop has a conversation { char buf[128]; // for text-to-speech text LcdClear(); LcdWrite(LCD_C, 0x80 ); // set X Command LcdWrite(LCD_C, 0x41 ); // set Y Command LcdString("ID "); critss = String(critr); critss.toCharArray(crits, 2); LcdString(crits); LcdString(" "); delay(400); // Check to see if time to send another message // if (news) { // news = 0; if (critr == 1) sprintf( buf, "[h0][f1][m51][t2][s7]I see one bee"); if (critr == 2) sprintf( buf, "[h0][f1][m51][t2][s7]I see another bee"); if (critr == 3) sprintf( buf, "[h0][f1][m53][t5][s6]Lora here, How are you?"); if (critr == 4) sprintf( buf, "[h0][f1][m51][t2][s7]Hello, this is Husky."); if (critr == 5) sprintf( buf, "[h0][f1][m51][t2][s7]I see a spyder"); if (critr == 6) sprintf( buf, "[h0][f1][m51][t2][s7]I see a Sicada"); if (critr == 7) sprintf( buf, "[h0][f1][m51][t2][s7]I see a Grasshopper"); if (critr == 8) sprintf( buf, "[h0][f1][m51][t2][s7]I see a Ladybug"); // if (critr == 9) sprintf( buf, "[f1][m53][t5][s5]Hey Cristal, Background check."); speak(buf); waitForSpeech(); LcdWrite(LCD_C, 0x80 ); // set X Command LcdWrite(LCD_C, 0x48 ); // set Y Command if (critr == 1) LcdString("OhBeeOne"); if (critr == 2) LcdString("Two Bee"); if (critr == 3) LcdString("Lora"); if (critr == 4) LcdString("Husky"); if (critr == 5) LcdString("Spiderman"); if (critr == 6) LcdString("Cindy Cicada"); if (critr == 7) LcdString("Grasshopper"); if (critr == 8) LcdString("Ladybug"); delay(400); if (critr == 1) sprintf( buf, "[h0][f1][m52][t4][s8]]Hi Husky, I am Oh bee wun!"); if (critr == 2) sprintf( buf, "[h0][f1][m53][t8][s7]Hi Husky, I am Too Bee!"); if (critr == 3) sprintf( buf, "[h0][f1][m53][t5][s6]Hey Husky, Lora here, Are you ready to recieve?"); if (critr == 4) sprintf( buf, "[h0][f1][m51][t2][s7]I an looking for bees bugs and beetles"); if (critr == 5) sprintf( buf, "[f1][m52][t6][s8]Hi Husky, I am Spyderman!"); if (critr == 6) sprintf( buf, "[f1][m3][t7][s7]Hi Husky, I am Sindy Sicada!"); if (critr == 7) sprintf( buf, "[f1][m20][t3][s8]Hi Husky, I am Grasshopper!"); if (critr == 8) sprintf( buf, "[f1][m55][t6][s6]Hi Husky, I am Ladybug!"); speak(buf); waitForSpeech(); delay(4000); //critter sequence if (critr == 8) critr = 4; if (critr == 7) critr = 8; if (critr == 6) critr = 7; if (critr == 5) critr = 6; if (critr == 2) critr = 5; if (critr == 1) critr = 2; if (critr == 4) critr = 1; LcdClear(); LcdWrite(LCD_C, 0x80 ); // set X Command LcdWrite(LCD_C, 0x41 ); // set Y Command LcdString("ID "); critss = String(critr); critss.toCharArray(crits, 2); LcdString(crits); LcdString(" "); // LoRa.beginPacket(); // LoRa.print(critr); // LoRa.endPacket(); // Now listen for response // while (packetSize = 0) { //this loop needs a get out of jail // packetSize = LoRa.parsePacket(); // } // LoRaChar = (char)LoRa.read(); // LcdWrite(LCD_C, 0x80 ); // set X Command // LcdWrite(LCD_C, 0x41 ); // set Y Command // LcdString("ACK "); // itoa(critr, crits, 2); // critss = String(critr); // critss.toCharArray(crits,2); // LcdString(crits); // LcdString(" "); // LcdWrite(LCD_C, 0x80 ); // set X Command // LcdWrite(LCD_C, 0x44 ); // set Y Command // LcdString("RSSI "); // rsi = (char)LoRa.packetRssi(); // rsis = String(rsi); // rsis.toCharArray(rsia,4); // LcdString(rsia); // LoRa.flush(); // delay(100); // news = 1; // } // } }
Discussion
Progress is being made towards a fully functional system, but there are hurdles with LoRa and I have not tackled the Nicla vision yet. These breadboard systems essentially use the same schematic as the one published in the previous blog. Unfortunately the PCBs have not arrived, so I have to suffer through breadboard lashups until they do. I thought the LoRa connection would be easy since I have done one before, but it looks like I have a non-functional radio and troubleshooting is difficult. It takes a surprising amount of time to adjust and tune up all the voices but they need to at least be somewhat understandable. It isn't enough to get the pitch and speed right, each word can require phonetic adjustment. It is kind of fun to see what is possible, but long passages of text will rapidly get tedious, given that you may need to listen to listen to a long segment, then reprogram it with a new guess, then do it all again. There are a bunch of options for each syllable, so it can take a lot of time to nail the effect you want.
Status and Next Steps - The PLan
1 The first blog introducing the project and the main characters with some speech synthesis is complete.
2 The second blog demonstrating machine learning of various insects and schematics of the interface PCB is complete.
3 This is the third blog which shows the build of 2 breadboard systems and programs that set up interfaces between the various sub-systems. It also introduces multi-voice synthesis.
4 Electronic systems build of 3 nodes, node software, 3D printed case design
5 Farmer system demonstration, Bee Keeper system demonstration, project summary and discussion
Links:
Save the Bees - Machine Learning
Image Conversion to Integer Array for LCD Display